diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/function_javascript_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/function_javascript_resource_gen.go index e633c26e0c..86fa5f5a52 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/function_javascript_resource_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/function_javascript_resource_gen.go @@ -93,7 +93,7 @@ func (f *FunctionJavascriptResourceAssert) HasNullInputBehaviorString(expected s } func (f *FunctionJavascriptResourceAssert) HasReturnBehaviorString(expected string) *FunctionJavascriptResourceAssert { - f.AddAssertion(assert.ValueSet("return_behavior", expected)) + f.AddAssertion(assert.ValueSet("return_results_behavior", expected)) return f } diff --git a/pkg/acceptance/bettertestspoc/assert/resourceassert/function_sql_resource_gen.go b/pkg/acceptance/bettertestspoc/assert/resourceassert/function_sql_resource_gen.go index 142de640a5..7a4188adeb 100644 --- a/pkg/acceptance/bettertestspoc/assert/resourceassert/function_sql_resource_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/resourceassert/function_sql_resource_gen.go @@ -93,7 +93,7 @@ func (f *FunctionSqlResourceAssert) HasNullInputBehaviorString(expected string) } func (f *FunctionSqlResourceAssert) HasReturnBehaviorString(expected string) *FunctionSqlResourceAssert { - f.AddAssertion(assert.ValueSet("return_behavior", expected)) + f.AddAssertion(assert.ValueSet("return_results_behavior", expected)) return f } diff --git a/pkg/acceptance/bettertestspoc/config/model/function_javascript_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/function_javascript_model_ext.go index 3fa63b5701..c1429191a6 100644 --- a/pkg/acceptance/bettertestspoc/config/model/function_javascript_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/function_javascript_model_ext.go @@ -2,6 +2,12 @@ package model import ( "encoding/json" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" ) func (f *FunctionJavascriptModel) MarshalJSON() ([]byte, error) { @@ -14,3 +20,24 @@ func (f *FunctionJavascriptModel) MarshalJSON() ([]byte, error) { DependsOn: f.DependsOn(), }) } + +func FunctionJavascriptInline(resourceName string, id sdk.SchemaObjectIdentifierWithArguments, functionDefinition string, returnType string) *FunctionJavascriptModel { + f := &FunctionJavascriptModel{ResourceModelMeta: config.Meta(resourceName, resources.FunctionJavascript)} + f.WithDatabase(id.DatabaseName()) + f.WithFunctionDefinition(functionDefinition) + f.WithName(id.Name()) + f.WithReturnType(returnType) + f.WithSchema(id.SchemaName()) + return f +} + +func (f *FunctionJavascriptModel) WithArgument(argName string, argDataType datatypes.DataType) *FunctionJavascriptModel { + return f.WithArgumentsValue( + tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "arg_name": tfconfig.StringVariable(argName), + "arg_data_type": tfconfig.StringVariable(argDataType.ToSql()), + }, + ), + ) +} diff --git a/pkg/acceptance/bettertestspoc/config/model/function_python_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/function_python_model_ext.go index 8d7475e389..f68dec8871 100644 --- a/pkg/acceptance/bettertestspoc/config/model/function_python_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/function_python_model_ext.go @@ -2,6 +2,11 @@ package model import ( "encoding/json" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" ) func (f *FunctionPythonModel) MarshalJSON() ([]byte, error) { @@ -14,3 +19,67 @@ func (f *FunctionPythonModel) MarshalJSON() ([]byte, error) { DependsOn: f.DependsOn(), }) } + +func FunctionPythonBasicInline(resourceName string, id sdk.SchemaObjectIdentifierWithArguments, runtimeVersion string, returnType datatypes.DataType, handler string, functionDefinition string) *FunctionPythonModel { + return FunctionPython(resourceName, id.DatabaseName(), handler, id.Name(), returnType.ToSql(), runtimeVersion, id.SchemaName()).WithFunctionDefinition(functionDefinition) +} + +func (f *FunctionPythonModel) WithArgument(argName string, argDataType datatypes.DataType) *FunctionPythonModel { + return f.WithArgumentsValue( + tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "arg_name": tfconfig.StringVariable(argName), + "arg_data_type": tfconfig.StringVariable(argDataType.ToSql()), + }, + ), + ) +} + +func (f *FunctionPythonModel) WithImports(imports ...sdk.NormalizedPath) *FunctionPythonModel { + return f.WithImportsValue( + tfconfig.SetVariable( + collections.Map(imports, func(imp sdk.NormalizedPath) tfconfig.Variable { + return tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "stage_location": tfconfig.StringVariable(imp.StageLocation), + "path_on_stage": tfconfig.StringVariable(imp.PathOnStage), + }, + ) + })..., + ), + ) +} + +func (f *FunctionPythonModel) WithExternalAccessIntegrations(ids ...sdk.AccountObjectIdentifier) *FunctionPythonModel { + return f.WithExternalAccessIntegrationsValue( + tfconfig.SetVariable( + collections.Map(ids, func(id sdk.AccountObjectIdentifier) tfconfig.Variable { return tfconfig.StringVariable(id.Name()) })..., + ), + ) +} + +func (f *FunctionPythonModel) WithSecrets(secrets map[string]sdk.SchemaObjectIdentifier) *FunctionPythonModel { + objects := make([]tfconfig.Variable, 0) + for k, v := range secrets { + objects = append(objects, tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "secret_variable_name": tfconfig.StringVariable(k), + "secret_id": tfconfig.StringVariable(v.FullyQualifiedName()), + }, + )) + } + + return f.WithSecretsValue( + tfconfig.SetVariable( + objects..., + ), + ) +} + +func (f *FunctionPythonModel) WithPackages(pkgs ...string) *FunctionPythonModel { + return f.WithPackagesValue( + tfconfig.SetVariable( + collections.Map(pkgs, func(pkg string) tfconfig.Variable { return tfconfig.StringVariable(pkg) })..., + ), + ) +} diff --git a/pkg/acceptance/bettertestspoc/config/model/function_scala_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/function_scala_model_ext.go index a5e43e53ca..bb7fcf2847 100644 --- a/pkg/acceptance/bettertestspoc/config/model/function_scala_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/function_scala_model_ext.go @@ -2,6 +2,12 @@ package model import ( "encoding/json" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" ) func (f *FunctionScalaModel) MarshalJSON() ([]byte, error) { @@ -14,3 +20,96 @@ func (f *FunctionScalaModel) MarshalJSON() ([]byte, error) { DependsOn: f.DependsOn(), }) } + +func FunctionScalaBasicInline( + resourceName string, + id sdk.SchemaObjectIdentifierWithArguments, + runtimeVersion string, + returnType datatypes.DataType, + handler string, + functionDefinition string, +) *FunctionScalaModel { + return FunctionScala(resourceName, id.DatabaseName(), handler, id.Name(), returnType.ToSql(), runtimeVersion, id.SchemaName()).WithFunctionDefinition(functionDefinition) +} + +func (f *FunctionScalaModel) WithArgument(argName string, argDataType datatypes.DataType) *FunctionScalaModel { + return f.WithArgumentsValue( + tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "arg_name": tfconfig.StringVariable(argName), + "arg_data_type": tfconfig.StringVariable(argDataType.ToSql()), + }, + ), + ) +} + +func (f *FunctionScalaModel) WithImport(stageLocation string, pathOnStage string) *FunctionScalaModel { + return f.WithImportsValue( + tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "stage_location": tfconfig.StringVariable(strings.TrimPrefix(stageLocation, "@")), + "path_on_stage": tfconfig.StringVariable(pathOnStage), + }, + ), + ) +} + +func (f *FunctionScalaModel) WithImports(imports ...sdk.NormalizedPath) *FunctionScalaModel { + return f.WithImportsValue( + tfconfig.SetVariable( + collections.Map(imports, func(imp sdk.NormalizedPath) tfconfig.Variable { + return tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "stage_location": tfconfig.StringVariable(imp.StageLocation), + "path_on_stage": tfconfig.StringVariable(imp.PathOnStage), + }, + ) + })..., + ), + ) +} + +func (f *FunctionScalaModel) WithPackages(pkgs ...string) *FunctionScalaModel { + return f.WithPackagesValue( + tfconfig.SetVariable( + collections.Map(pkgs, func(pkg string) tfconfig.Variable { return tfconfig.StringVariable(pkg) })..., + ), + ) +} + +func (f *FunctionScalaModel) WithExternalAccessIntegrations(ids ...sdk.AccountObjectIdentifier) *FunctionScalaModel { + return f.WithExternalAccessIntegrationsValue( + tfconfig.SetVariable( + collections.Map(ids, func(id sdk.AccountObjectIdentifier) tfconfig.Variable { return tfconfig.StringVariable(id.Name()) })..., + ), + ) +} + +func (f *FunctionScalaModel) WithSecrets(secrets map[string]sdk.SchemaObjectIdentifier) *FunctionScalaModel { + objects := make([]tfconfig.Variable, 0) + for k, v := range secrets { + objects = append(objects, tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "secret_variable_name": tfconfig.StringVariable(k), + "secret_id": tfconfig.StringVariable(v.FullyQualifiedName()), + }, + )) + } + + return f.WithSecretsValue( + tfconfig.SetVariable( + objects..., + ), + ) +} + +func (f *FunctionScalaModel) WithTargetPathParts(stageLocation string, pathOnStage string) *FunctionScalaModel { + return f.WithTargetPathValue( + tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "stage_location": tfconfig.StringVariable(stageLocation), + "path_on_stage": tfconfig.StringVariable(pathOnStage), + }, + ), + ) +} diff --git a/pkg/acceptance/bettertestspoc/config/model/function_sql_model_ext.go b/pkg/acceptance/bettertestspoc/config/model/function_sql_model_ext.go index d4f775628d..f01b2e8ffe 100644 --- a/pkg/acceptance/bettertestspoc/config/model/function_sql_model_ext.go +++ b/pkg/acceptance/bettertestspoc/config/model/function_sql_model_ext.go @@ -2,6 +2,12 @@ package model import ( "encoding/json" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + tfconfig "github.com/hashicorp/terraform-plugin-testing/config" ) func (f *FunctionSqlModel) MarshalJSON() ([]byte, error) { @@ -14,3 +20,24 @@ func (f *FunctionSqlModel) MarshalJSON() ([]byte, error) { DependsOn: f.DependsOn(), }) } + +func FunctionSqlBasicInline(resourceName string, id sdk.SchemaObjectIdentifierWithArguments, functionDefinition string, returnType string) *FunctionSqlModel { + f := &FunctionSqlModel{ResourceModelMeta: config.Meta(resourceName, resources.FunctionSql)} + f.WithDatabase(id.DatabaseName()) + f.WithFunctionDefinition(functionDefinition) + f.WithName(id.Name()) + f.WithReturnType(returnType) + f.WithSchema(id.SchemaName()) + return f +} + +func (f *FunctionSqlModel) WithArgument(argName string, argDataType datatypes.DataType) *FunctionSqlModel { + return f.WithArgumentsValue( + tfconfig.ObjectVariable( + map[string]tfconfig.Variable{ + "arg_name": tfconfig.StringVariable(argName), + "arg_data_type": tfconfig.StringVariable(argDataType.ToSql()), + }, + ), + ) +} diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 36c5ffcfb4..600027fe2c 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -240,6 +240,11 @@ func (c *FunctionClient) SampleJavascriptDefinition(t *testing.T, argName string `, argName) } +func (c *FunctionClient) SampleJavascriptDefinitionNoArgs(t *testing.T) string { + t.Helper() + return `return 1;` +} + func (c *FunctionClient) SamplePythonDefinition(t *testing.T, funcName string, argName string) string { t.Helper() @@ -271,6 +276,14 @@ func (c *FunctionClient) SampleSqlDefinition(t *testing.T) string { return "3.141592654::FLOAT" } +func (c *FunctionClient) SampleSqlDefinitionWithArgument(t *testing.T, argName string) string { + t.Helper() + + return fmt.Sprintf(` +%s +`, argName) +} + func (c *FunctionClient) PythonIdentityDefinition(t *testing.T, funcName string, argName string) string { t.Helper() diff --git a/pkg/resources/function_java_acceptance_test.go b/pkg/resources/function_java_acceptance_test.go index 8e30bf28e4..e210974888 100644 --- a/pkg/resources/function_java_acceptance_test.go +++ b/pkg/resources/function_java_acceptance_test.go @@ -234,7 +234,7 @@ func TestAcc_FunctionJava_InlineFull(t *testing.T) { }). WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). WithRuntimeVersion("11"). - WithIsSecure("false"). + WithIsSecure(r.BooleanFalse). WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). WithComment("some comment") diff --git a/pkg/resources/function_javascript_acceptance_test.go b/pkg/resources/function_javascript_acceptance_test.go new file mode 100644 index 0000000000..2ef1c7d7f7 --- /dev/null +++ b/pkg/resources/function_javascript_acceptance_test.go @@ -0,0 +1,194 @@ +package resources_test + +import ( + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" + "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/testdatatypes" + "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" +) + +func TestAcc_FunctionJavascript_InlineBasic(t *testing.T) { + argName := "x" + dataType := testdatatypes.DataTypeVariant + + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + idWithChangedNameButTheSameDataType := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + + definition := acc.TestClient().Function.SampleJavascriptDefinition(t, argName) + + functionModel := model.FunctionJavascriptInline("test", id, definition, datatypes.VariantLegacyDataType). + WithArgument(argName, dataType) + functionModelRenamed := model.FunctionJavascriptInline("test", idWithChangedNameButTheSameDataType, definition, datatypes.VariantLegacyDataType). + WithArgument(argName, dataType) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionJavascript), + Steps: []resource.TestStep{ + // CREATE BASIC + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionJavascriptResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasReturnTypeString(datatypes.VariantLegacyDataType). + HasIsSecureString(r.BooleanDefault). + HasCommentString(sdk.DefaultFunctionComment). + HasFunctionDefinitionString(definition). + HasFunctionLanguageString("JAVASCRIPT"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_name", argName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_data_type", dataType.ToSql())), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_default_value", "")), + ), + }, + // RENAME + { + Config: config.FromModels(t, functionModelRenamed), + Check: assert.AssertThat(t, + resourceassert.FunctionJavaResource(t, functionModelRenamed.ResourceReference()). + HasNameString(idWithChangedNameButTheSameDataType.Name()). + HasFullyQualifiedNameString(idWithChangedNameButTheSameDataType.FullyQualifiedName()), + ), + }, + }, + }) +} + +func TestAcc_FunctionJavascript_InlineEmptyArgs(t *testing.T) { + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes() + definition := acc.TestClient().Function.SampleJavascriptDefinitionNoArgs(t) + functionModel := model.FunctionJavascriptInline("test", id, definition, datatypes.VariantLegacyDataType) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionJavascript), + Steps: []resource.TestStep{ + // CREATE BASIC + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionJavaResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasFunctionDefinitionString(definition). + HasFunctionLanguageString("JAVASCRIPT"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + ), + }, + }, + }) +} + +func TestAcc_FunctionJavascript_InlineFull(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + argName := "x" + dataType := testdatatypes.DataTypeVariant + + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + definition := acc.TestClient().Function.SampleJavascriptDefinition(t, argName) + functionModel := model.FunctionJavascriptInline("test", id, definition, datatypes.VariantLegacyDataType). + WithIsSecure(r.BooleanFalse). + WithArgument(argName, dataType). + WithNullInputBehavior(string(sdk.NullInputBehaviorReturnsNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment("some comment") + + functionModelUpdateWithoutRecreation := model.FunctionJavascriptInline("test", id, definition, datatypes.VariantLegacyDataType). + WithArgument(argName, dataType). + WithIsSecure(r.BooleanFalse). + WithNullInputBehavior(string(sdk.NullInputBehaviorReturnsNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment("some other comment") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionJavascript), + Steps: []resource.TestStep{ + // CREATE WITH ALL + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionJavascriptResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasFunctionDefinitionString(definition). + HasCommentString("some comment"). + HasFunctionLanguageString("JAVASCRIPT"). + HasNullInputBehaviorString(string(sdk.NullInputBehaviorReturnsNullInput)). + HasReturnBehaviorString(string(sdk.ReturnResultsBehaviorVolatile)). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"arguments.0.arg_data_type"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionJavaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARIANT")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // UPDATE WITHOUT RECREATION + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(functionModelUpdateWithoutRecreation.ResourceReference(), plancheck.ResourceActionUpdate), + }, + }, + Config: config.FromModels(t, functionModelUpdateWithoutRecreation), + Check: assert.AssertThat(t, + resourceassert.FunctionJavascriptResource(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasFunctionDefinitionString(definition). + HasCommentString("some other comment"). + HasFunctionLanguageString("JAVASCRIPT"). + HasNullInputBehaviorString(string(sdk.NullInputBehaviorReturnsNullInput)). + HasReturnBehaviorString(string(sdk.ReturnResultsBehaviorVolatile)). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasIsSecure(false), + ), + }, + }, + }) +} diff --git a/pkg/resources/function_python_acceptance_test.go b/pkg/resources/function_python_acceptance_test.go new file mode 100644 index 0000000000..04c82d91e5 --- /dev/null +++ b/pkg/resources/function_python_acceptance_test.go @@ -0,0 +1,229 @@ +package resources_test + +import ( + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" + "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/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "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/tfversion" +) + +func TestAcc_FunctionPython_InlineBasic(t *testing.T) { + funcName := "some_function" + argName := "x" + dataType := testdatatypes.DataTypeNumber_36_2 + + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + idWithChangedNameButTheSameDataType := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + + definition := acc.TestClient().Function.SamplePythonDefinition(t, funcName, argName) + + functionModel := model.FunctionPythonBasicInline("test", id, "3.8", dataType, funcName, definition). + WithArgument(argName, dataType) + functionModelRenamed := model.FunctionPythonBasicInline("test", idWithChangedNameButTheSameDataType, "3.8", dataType, funcName, definition). + WithArgument(argName, dataType) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionPython), + Steps: []resource.TestStep{ + // CREATE BASIC + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionPythonResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanDefault). + HasCommentString(sdk.DefaultFunctionComment). + HasRuntimeVersionString("3.8"). + HasFunctionDefinitionString(definition). + HasFunctionLanguageString("PYTHON"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_name", argName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_data_type", dataType.ToSql())), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_default_value", "")), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"arguments.0.arg_data_type", "is_aggregate", "is_secure", "null_input_behavior", "return_results_behavior"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionPythonResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "NUMBER(38, 0)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // RENAME + { + Config: config.FromModels(t, functionModelRenamed), + Check: assert.AssertThat(t, + resourceassert.FunctionPythonResource(t, functionModelRenamed.ResourceReference()). + HasNameString(idWithChangedNameButTheSameDataType.Name()). + HasFullyQualifiedNameString(idWithChangedNameButTheSameDataType.FullyQualifiedName()), + ), + }, + }, + }) +} + +func TestAcc_FunctionPython_InlineFull(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + secretId := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + secretId2 := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + + networkRule, networkRuleCleanup := acc.TestClient().NetworkRule.Create(t) + t.Cleanup(networkRuleCleanup) + + secret, secretCleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId, "test_secret_string") + t.Cleanup(secretCleanup) + + secret2, secret2Cleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId2, "test_secret_string_2") + t.Cleanup(secret2Cleanup) + + externalAccessIntegration, externalAccessIntegrationCleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) + t.Cleanup(externalAccessIntegrationCleanup) + + externalAccessIntegration2, externalAccessIntegration2Cleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret2.ID()) + t.Cleanup(externalAccessIntegration2Cleanup) + + tmpPythonFunction := acc.TestClient().CreateSamplePythonFunctionAndModule(t) + tmpPythonFunction2 := acc.TestClient().CreateSamplePythonFunctionAndModule(t) + + funcName := "some_function" + argName := "x" + dataType := testdatatypes.DataTypeNumber_36_2 + + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + definition := acc.TestClient().Function.SamplePythonDefinition(t, funcName, argName) + + functionModel := model.FunctionPythonBasicInline("test", id, "3.8", dataType, funcName, definition). + WithIsSecure(r.BooleanFalse). + WithArgument(argName, dataType). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment("some comment"). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpPythonFunction.ModuleName + ".py"}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpPythonFunction2.ModuleName + ".py"}, + ). + WithPackages("numpy", "pandas"). + WithExternalAccessIntegrations(externalAccessIntegration, externalAccessIntegration2). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "abc": secretId, + "def": secretId2, + }) + + functionModelUpdateWithoutRecreation := model.FunctionPythonBasicInline("test", id, "3.8", dataType, funcName, definition). + WithIsSecure(r.BooleanFalse). + WithArgument(argName, dataType). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment("some other comment"). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpPythonFunction.ModuleName + ".py"}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpPythonFunction2.ModuleName + ".py"}, + ). + WithPackages("numpy", "pandas"). + WithExternalAccessIntegrations(externalAccessIntegration). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "def": secretId2, + }) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionPython), + Steps: []resource.TestStep{ + // CREATE WITH ALL + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionPythonResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasRuntimeVersionString("3.8"). + HasFunctionDefinitionString(definition). + HasCommentString("some comment"). + HasFunctionLanguageString("PYTHON"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "secrets.#", "2")), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "external_access_integrations.#", "2")), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "packages.#", "2")), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"is_aggregate", "arguments.0.arg_data_type"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionPythonResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "NUMBER(38, 0)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // UPDATE WITHOUT RECREATION + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(functionModelUpdateWithoutRecreation.ResourceReference(), plancheck.ResourceActionUpdate), + }, + }, + Config: config.FromModels(t, functionModelUpdateWithoutRecreation), + Check: assert.AssertThat(t, + resourceassert.FunctionPythonResource(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasRuntimeVersionString("3.8"). + HasFunctionDefinitionString(definition). + HasCommentString("some other comment"). + HasFunctionLanguageString("PYTHON"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.#", "1")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_variable_name", "def")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_id", secretId2.FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.#", "1")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.0", externalAccessIntegration.Name())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "packages.#", "2")), + resourceshowoutputassert.FunctionShowOutput(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasIsSecure(false), + ), + }, + }, + }) +} diff --git a/pkg/resources/function_scala.go b/pkg/resources/function_scala.go index c323a54a27..8331b63c16 100644 --- a/pkg/resources/function_scala.go +++ b/pkg/resources/function_scala.go @@ -65,7 +65,7 @@ func CreateContextFunctionScala(ctx context.Context, d *schema.ResourceData, met argumentDataTypes := collections.Map(argumentRequests, func(r sdk.FunctionArgumentRequest) datatypes.DataType { return r.ArgDataType }) id := sdk.NewSchemaObjectIdentifierWithArgumentsNormalized(database, sc, name, argumentDataTypes...) - request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), returnDataType, runtimeVersion, handler). + request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), returnDataType, handler, runtimeVersion). WithArguments(argumentRequests) errs := errors.Join( diff --git a/pkg/resources/function_scala_acceptance_test.go b/pkg/resources/function_scala_acceptance_test.go new file mode 100644 index 0000000000..2dbc466a4a --- /dev/null +++ b/pkg/resources/function_scala_acceptance_test.go @@ -0,0 +1,248 @@ +package resources_test + +import ( + "fmt" + "testing" + "time" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" + "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/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "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" +) + +func TestAcc_FunctionScala_InlineBasic(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 + + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + idWithChangedNameButTheSameDataType := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := acc.TestClient().Function.SampleScalaDefinition(t, className, funcName, argName) + + functionModel := model.FunctionScalaBasicInline("test", id, "2.12", dataType, handler, definition). + WithArgument(argName, dataType) + functionModelRenamed := model.FunctionScalaBasicInline("test", idWithChangedNameButTheSameDataType, "2.12", dataType, handler, definition). + WithArgument(argName, dataType) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionScala), + Steps: []resource.TestStep{ + // CREATE BASIC + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionScalaResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanDefault). + HasCommentString(sdk.DefaultFunctionComment). + HasRuntimeVersionString("2.12"). + HasFunctionDefinitionString(definition). + HasFunctionLanguageString("SCALA"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_name", argName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_data_type", dataType.ToSql())), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_default_value", "")), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"arguments.0.arg_data_type", "is_secure", "null_input_behavior", "return_results_behavior"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionScalaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARCHAR(16777216)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // RENAME + { + Config: config.FromModels(t, functionModelRenamed), + Check: assert.AssertThat(t, + resourceassert.FunctionJavaResource(t, functionModelRenamed.ResourceReference()). + HasNameString(idWithChangedNameButTheSameDataType.Name()). + HasFullyQualifiedNameString(idWithChangedNameButTheSameDataType.FullyQualifiedName()), + ), + }, + }, + }) +} + +func TestAcc_FunctionScala_InlineFull(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.EnableAcceptance) + acc.TestAccPreCheck(t) + + stage, stageCleanup := acc.TestClient().Stage.CreateStage(t) + t.Cleanup(stageCleanup) + + secretId := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + secretId2 := acc.TestClient().Ids.RandomSchemaObjectIdentifier() + + networkRule, networkRuleCleanup := acc.TestClient().NetworkRule.Create(t) + t.Cleanup(networkRuleCleanup) + + secret, secretCleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId, "test_secret_string") + t.Cleanup(secretCleanup) + + secret2, secret2Cleanup := acc.TestClient().Secret.CreateWithGenericString(t, secretId2, "test_secret_string_2") + t.Cleanup(secret2Cleanup) + + externalAccessIntegration, externalAccessIntegrationCleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) + t.Cleanup(externalAccessIntegrationCleanup) + + externalAccessIntegration2, externalAccessIntegration2Cleanup := acc.TestClient().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret2.ID()) + t.Cleanup(externalAccessIntegration2Cleanup) + + tmpJavaFunction := acc.TestClient().CreateSampleJavaFunctionAndJarOnUserStage(t) + tmpJavaFunction2 := acc.TestClient().CreateSampleJavaFunctionAndJarOnUserStage(t) + + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 + + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := acc.TestClient().Function.SampleScalaDefinition(t, className, funcName, argName) + // TODO [SNOW-1850370]: extract to helper + jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) + + functionModel := model.FunctionScalaBasicInline("test", id, "2.12", dataType, handler, definition). + WithArgument(argName, dataType). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction.JarName}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction2.JarName}, + ). + WithPackages("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). + WithExternalAccessIntegrations(externalAccessIntegration, externalAccessIntegration2). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "abc": secretId, + "def": secretId2, + }). + WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). + WithIsSecure(r.BooleanFalse). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment("some comment") + + functionModelUpdateWithoutRecreation := model.FunctionScalaBasicInline("test", id, "2.12", dataType, handler, definition). + WithArgument(argName, dataType). + WithImports( + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction.JarName}, + sdk.NormalizedPath{StageLocation: "~", PathOnStage: tmpJavaFunction2.JarName}, + ). + WithPackages("com.snowflake:snowpark:1.14.0", "com.snowflake:telemetry:0.1.0"). + WithExternalAccessIntegrations(externalAccessIntegration). + WithSecrets(map[string]sdk.SchemaObjectIdentifier{ + "def": secretId2, + }). + WithTargetPathParts(stage.ID().FullyQualifiedName(), jarName). + WithIsSecure(r.BooleanFalse). + WithNullInputBehavior(string(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment("some other comment") + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionScala), + Steps: []resource.TestStep{ + // CREATE WITH ALL + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionScalaResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasRuntimeVersionString("2.12"). + HasFunctionDefinitionString(definition). + HasCommentString("some comment"). + HasFunctionLanguageString("SCALA"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "target_path.0.stage_location", stage.ID().FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "target_path.0.path_on_stage", jarName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "secrets.#", "2")), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "external_access_integrations.#", "2")), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "packages.#", "2")), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"arguments.0.arg_data_type"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionScalaResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "VARCHAR(16777216)")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // UPDATE WITHOUT RECREATION + { + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(functionModelUpdateWithoutRecreation.ResourceReference(), plancheck.ResourceActionUpdate), + }, + }, + Config: config.FromModels(t, functionModelUpdateWithoutRecreation), + Check: assert.AssertThat(t, + resourceassert.FunctionScalaResource(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasRuntimeVersionString("2.12"). + HasFunctionDefinitionString(definition). + HasCommentString("some other comment"). + HasFunctionLanguageString("SCALA"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "target_path.0.stage_location", stage.ID().FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "target_path.0.path_on_stage", jarName)), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.#", "1")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_variable_name", "def")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "secrets.0.secret_id", secretId2.FullyQualifiedName())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.#", "1")), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "external_access_integrations.0", externalAccessIntegration.Name())), + assert.Check(resource.TestCheckResourceAttr(functionModelUpdateWithoutRecreation.ResourceReference(), "packages.#", "2")), + resourceshowoutputassert.FunctionShowOutput(t, functionModelUpdateWithoutRecreation.ResourceReference()). + HasIsSecure(false), + ), + }, + }, + }) +} diff --git a/pkg/resources/function_sql_acceptance_test.go b/pkg/resources/function_sql_acceptance_test.go new file mode 100644 index 0000000000..c8df1348ed --- /dev/null +++ b/pkg/resources/function_sql_acceptance_test.go @@ -0,0 +1,164 @@ +package resources_test + +import ( + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + r "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert" + "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/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "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/tfversion" +) + +func TestAcc_FunctionSql_InlineBasic(t *testing.T) { + argName := "abc" + dataType := testdatatypes.DataTypeFloat + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + idWithChangedNameButTheSameDataType := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes() + + definition := acc.TestClient().Function.SampleSqlDefinitionWithArgument(t, argName) + + functionModel := model.FunctionSqlBasicInline("test", id, definition, dataType.ToLegacyDataTypeSql()). + WithArgument(argName, dataType) + functionModelRenamed := model.FunctionSqlBasicInline("test", idWithChangedNameButTheSameDataType, definition, dataType.ToLegacyDataTypeSql()). + WithArgument(argName, dataType) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionSql), + Steps: []resource.TestStep{ + // CREATE BASIC + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionSqlResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanDefault). + HasCommentString(sdk.DefaultFunctionComment). + HasFunctionDefinitionString(definition). + HasFunctionLanguageString("SQL"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_name", argName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_data_type", dataType.ToSql())), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_default_value", "")), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionSqlResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "FLOAT")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // RENAME + { + Config: config.FromModels(t, functionModelRenamed), + Check: assert.AssertThat(t, + resourceassert.FunctionSqlResource(t, functionModelRenamed.ResourceReference()). + HasNameString(idWithChangedNameButTheSameDataType.Name()). + HasFullyQualifiedNameString(idWithChangedNameButTheSameDataType.FullyQualifiedName()), + ), + }, + }, + }) +} + +func TestAcc_FunctionSql_InlineFull(t *testing.T) { + argName := "abc" + comment := random.Comment() + otherComment := random.Comment() + dataType := testdatatypes.DataTypeFloat + id := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + idWithChangedNameButTheSameDataType := acc.TestClient().Ids.RandomSchemaObjectIdentifierWithArgumentsNewDataTypes(dataType) + + definition := acc.TestClient().Function.SampleSqlDefinitionWithArgument(t, argName) + + functionModel := model.FunctionSqlBasicInline("test", id, definition, dataType.ToLegacyDataTypeSql()). + WithIsSecure(r.BooleanFalse). + WithArgument(argName, dataType). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment(comment) + functionModelRenamed := model.FunctionSqlBasicInline("test", idWithChangedNameButTheSameDataType, definition, dataType.ToLegacyDataTypeSql()). + WithIsSecure(r.BooleanFalse). + WithArgument(argName, dataType). + WithReturnResultsBehavior(string(sdk.ReturnResultsBehaviorVolatile)). + WithComment(otherComment) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + PreCheck: func() { acc.TestAccPreCheck(t) }, + CheckDestroy: acc.CheckDestroy(t, resources.FunctionSql), + Steps: []resource.TestStep{ + // CREATE BASIC + { + Config: config.FromModels(t, functionModel), + Check: assert.AssertThat(t, + resourceassert.FunctionSqlResource(t, functionModel.ResourceReference()). + HasNameString(id.Name()). + HasIsSecureString(r.BooleanFalse). + HasCommentString(comment). + HasReturnBehaviorString(string(sdk.ReturnResultsBehaviorVolatile)). + HasFunctionDefinitionString(definition). + HasFunctionLanguageString("SQL"). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + resourceshowoutputassert.FunctionShowOutput(t, functionModel.ResourceReference()). + HasIsSecure(false), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_name", argName)), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_data_type", dataType.ToSql())), + assert.Check(resource.TestCheckResourceAttr(functionModel.ResourceReference(), "arguments.0.arg_default_value", "")), + ), + }, + // IMPORT + { + ResourceName: functionModel.ResourceReference(), + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"return_results_behavior"}, + ImportStateCheck: assert.AssertThatImport(t, + resourceassert.ImportedFunctionSqlResource(t, id.FullyQualifiedName()). + HasFullyQualifiedNameString(id.FullyQualifiedName()), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_name", argName)), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_data_type", "FLOAT")), + assert.CheckImport(importchecks.TestCheckResourceAttrInstanceState(helpers.EncodeResourceIdentifier(id), "arguments.0.arg_default_value", "")), + ), + }, + // RENAME + { + Config: config.FromModels(t, functionModelRenamed), + Check: assert.AssertThat(t, + resourceassert.FunctionSqlResource(t, functionModelRenamed.ResourceReference()). + HasNameString(idWithChangedNameButTheSameDataType.Name()). + HasFullyQualifiedNameString(idWithChangedNameButTheSameDataType.FullyQualifiedName()). + HasCommentString(otherComment), + ), + }, + }, + }) +} diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index b5e9a35fb6..1036429d78 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -2053,12 +2053,13 @@ func TestInt_Functions(t *testing.T) { t.Run("create function for SQL - return table data type", func(t *testing.T) { argName := "x" - returnDataType, err := datatypes.ParseDataType(fmt.Sprintf("TABLE(ID %s, PRICE %s, THIRD %s)", datatypes.NumberLegacyDataType, datatypes.FloatLegacyDataType, datatypes.VarcharLegacyDataType)) + returnDataType, err := datatypes.ParseDataType(fmt.Sprintf("TABLE(PRICE %s, THIRD %s)", datatypes.FloatLegacyDataType, datatypes.VarcharLegacyDataType)) require.NoError(t, err) id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(datatypes.VarcharLegacyDataType) - definition := ` SELECT 1, 2.2::float, 'abc';` + definition := ` +SELECT 2.2::float, 'abc');` // the ending parenthesis has to be there (otherwise SQL compilation error is thrown) dt := sdk.NewFunctionReturnsResultDataTypeRequest(returnDataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) argument := sdk.NewFunctionArgumentRequest(argName, nil).WithArgDataTypeOld(datatypes.VarcharLegacyDataType) @@ -2076,7 +2077,7 @@ func TestInt_Functions(t *testing.T) { HasCreatedOnNotEmpty(). HasName(id.Name()). HasSchemaName(id.SchemaName()). - HasArgumentsRawContains(returnDataType.ToLegacyDataTypeSql()), + HasArgumentsRawContains(strings.ReplaceAll(returnDataType.ToLegacyDataTypeSql(), "TABLE(", "TABLE (")), ) assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id).