From 1698ed595a0ac0f9ea939de2e0863d02ad54a39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Cie=C5=9Blak?= Date: Tue, 30 Jul 2024 11:50:19 +0200 Subject: [PATCH] Add integration tests for identifiers --- pkg/acceptance/helpers/grant_client.go | 45 +++++ pkg/acceptance/helpers/test_client.go | 2 + .../secondary_database_acceptance_test.go | 4 +- pkg/sdk/identifier_parsers.go | 5 + pkg/sdk/identifier_parsers_test.go | 26 +-- .../testint/identifier_integration_test.go | 163 ++++++++++++++++++ .../identifier_parsers_integration_test.go | 18 -- 7 files changed, 231 insertions(+), 32 deletions(-) create mode 100644 pkg/acceptance/helpers/grant_client.go create mode 100644 pkg/sdk/testint/identifier_integration_test.go delete mode 100644 pkg/sdk/testint/identifier_parsers_integration_test.go diff --git a/pkg/acceptance/helpers/grant_client.go b/pkg/acceptance/helpers/grant_client.go new file mode 100644 index 0000000000..fa7ba85ccc --- /dev/null +++ b/pkg/acceptance/helpers/grant_client.go @@ -0,0 +1,45 @@ +package helpers + +import ( + "context" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/stretchr/testify/require" +) + +type GrantClient struct { + context *TestClientContext + ids *IdsGenerator +} + +func NewGrantClient(context *TestClientContext, idsGenerator *IdsGenerator) *GrantClient { + return &GrantClient{ + context: context, + ids: idsGenerator, + } +} + +func (c *GrantClient) client() sdk.Grants { + return c.context.client.Grants +} + +func (c *GrantClient) GrantOnSchemaToAccountRole(t *testing.T, schemaId sdk.DatabaseObjectIdentifier, accountRoleId sdk.AccountObjectIdentifier, privileges ...sdk.SchemaPrivilege) { + t.Helper() + ctx := context.Background() + + err := c.client().GrantPrivilegesToAccountRole( + ctx, + &sdk.AccountRoleGrantPrivileges{ + SchemaPrivileges: privileges, + }, + &sdk.AccountRoleGrantOn{ + Schema: &sdk.GrantOnSchema{ + Schema: &schemaId, + }, + }, + accountRoleId, + new(sdk.GrantPrivilegesToAccountRoleOptions), + ) + require.NoError(t, err) +} diff --git a/pkg/acceptance/helpers/test_client.go b/pkg/acceptance/helpers/test_client.go index ad1b2a982b..c629c37a0e 100644 --- a/pkg/acceptance/helpers/test_client.go +++ b/pkg/acceptance/helpers/test_client.go @@ -24,6 +24,7 @@ type TestClient struct { ExternalVolume *ExternalVolumeClient FailoverGroup *FailoverGroupClient FileFormat *FileFormatClient + Grant *GrantClient MaskingPolicy *MaskingPolicyClient MaterializedView *MaterializedViewClient NetworkPolicy *NetworkPolicyClient @@ -77,6 +78,7 @@ func NewTestClient(c *sdk.Client, database string, schema string, warehouse stri ExternalVolume: NewExternalVolumeClient(context, idsGenerator), FailoverGroup: NewFailoverGroupClient(context, idsGenerator), FileFormat: NewFileFormatClient(context, idsGenerator), + Grant: NewGrantClient(context, idsGenerator), MaskingPolicy: NewMaskingPolicyClient(context, idsGenerator), MaterializedView: NewMaterializedViewClient(context, idsGenerator), NetworkPolicy: NewNetworkPolicyClient(context, idsGenerator), diff --git a/pkg/resources/secondary_database_acceptance_test.go b/pkg/resources/secondary_database_acceptance_test.go index e14bc9f593..df289b6e6a 100644 --- a/pkg/resources/secondary_database_acceptance_test.go +++ b/pkg/resources/secondary_database_acceptance_test.go @@ -162,7 +162,7 @@ func TestAcc_CreateSecondaryDatabase_complete(t *testing.T) { sdk.NewAccountIdentifierFromAccountLocator(acc.Client(t).GetAccountLocator()), }) t.Cleanup(func() { - // TODO(SNOW-1562172): Create a better solution for this type of situations + // TODO(SNOW-1562172: Create a better solution for this type of situations // Have to wait; otherwise the secondary database removal can be not registered yet, // resulting in an error in the cleanup below. time.Sleep(time.Second) @@ -408,7 +408,7 @@ func TestAcc_CreateSecondaryDatabase_DataRetentionTimeInDays(t *testing.T) { sdk.NewAccountIdentifierFromAccountLocator(acc.Client(t).GetAccountLocator()), }) t.Cleanup(func() { - // TODO(SNOW-1562172): Create a better solution for this type of situations + // TODO(SNOW-1562172: Create a better solution for this type of situations // Have to wait; otherwise the secondary database removal can be not registered yet, // resulting in an error in the cleanup below. time.Sleep(time.Second) diff --git a/pkg/sdk/identifier_parsers.go b/pkg/sdk/identifier_parsers.go index 2265fd43d7..ae3677d894 100644 --- a/pkg/sdk/identifier_parsers.go +++ b/pkg/sdk/identifier_parsers.go @@ -21,6 +21,11 @@ func ParseIdentifierStringWithOpts(identifier string, opts func(*csv.Reader)) ([ if len(lines) != 1 { return nil, fmt.Errorf("incompatible identifier: %s", identifier) } + for _, part := range lines[0] { + if strings.Contains(part, `"`) { + return nil, fmt.Errorf(`unable to parse identifier: %s, currently identifiers containing double quotes are not supported in the provider`, identifier) + } + } return lines[0], nil } diff --git a/pkg/sdk/identifier_parsers_test.go b/pkg/sdk/identifier_parsers_test.go index de11a58b54..6887cecf0b 100644 --- a/pkg/sdk/identifier_parsers_test.go +++ b/pkg/sdk/identifier_parsers_test.go @@ -125,72 +125,72 @@ func Test_IdentifierParsers(t *testing.T) { {IdentifierType: "AccountObjectIdentifier", Input: "a\nb", Error: "incompatible identifier: a\nb"}, {IdentifierType: "AccountObjectIdentifier", Input: `a"b`, Error: "unable to read identifier: a\"b, err = parse error on line 1, column 2: bare \" in non-quoted-field"}, {IdentifierType: "AccountObjectIdentifier", Input: `abc.cde`, Error: `unexpected number of parts 2 in identifier abc.cde, expected 1 in a form of ""`}, + {IdentifierType: "AccountObjectIdentifier", Input: `""""`, Error: `unable to parse identifier: """", currently identifiers containing double quotes are not supported in the provider`}, + {IdentifierType: "AccountObjectIdentifier", Input: `"a""bc"`, Error: `unable to parse identifier: "a""b", currently identifiers containing double quotes are not supported in the provider`}, {IdentifierType: "AccountObjectIdentifier", Input: `""`, Expected: NewAccountObjectIdentifier(``)}, - {IdentifierType: "AccountObjectIdentifier", Input: `""""`, Expected: NewAccountObjectIdentifier(`"`)}, {IdentifierType: "AccountObjectIdentifier", Input: `abc`, Expected: NewAccountObjectIdentifier(`abc`)}, {IdentifierType: "AccountObjectIdentifier", Input: `"abc"`, Expected: NewAccountObjectIdentifier(`abc`)}, {IdentifierType: "AccountObjectIdentifier", Input: `"ab.c"`, Expected: NewAccountObjectIdentifier(`ab.c`)}, - {IdentifierType: "AccountObjectIdentifier", Input: `"a""bc"`, Expected: NewAccountObjectIdentifier(`a"bc`)}, {IdentifierType: "DatabaseObjectIdentifier", Input: ``, Error: "incompatible identifier: "}, {IdentifierType: "DatabaseObjectIdentifier", Input: "a\nb.cde", Error: "unable to read identifier: a\nb.cde, err = record on line 2: wrong number of fields"}, {IdentifierType: "DatabaseObjectIdentifier", Input: `a"b.cde`, Error: "unable to read identifier: a\"b.cde, err = parse error on line 1, column 2: bare \" in non-quoted-field"}, {IdentifierType: "DatabaseObjectIdentifier", Input: `abc.cde.efg`, Error: `unexpected number of parts 3 in identifier abc.cde.efg, expected 2 in a form of "."`}, {IdentifierType: "DatabaseObjectIdentifier", Input: `abc`, Error: `unexpected number of parts 1 in identifier abc, expected 2 in a form of "."`}, + {IdentifierType: "DatabaseObjectIdentifier", Input: `"""".""""`, Error: `unable to parse identifier: """"."""", currently identifiers containing double quotes are not supported in the provider`}, + {IdentifierType: "DatabaseObjectIdentifier", Input: `"a""bc"."cd""e"`, Error: `unable to parse identifier: "a""bc"."cd""e", currently identifiers containing double quotes are not supported in the provider`}, {IdentifierType: "DatabaseObjectIdentifier", Input: `"".""`, Expected: NewDatabaseObjectIdentifier(``, ``)}, - {IdentifierType: "DatabaseObjectIdentifier", Input: `"""".""""`, Expected: NewDatabaseObjectIdentifier(`"`, `"`)}, {IdentifierType: "DatabaseObjectIdentifier", Input: `abc.cde`, Expected: NewDatabaseObjectIdentifier(`abc`, `cde`)}, {IdentifierType: "DatabaseObjectIdentifier", Input: `"abc"."cde"`, Expected: NewDatabaseObjectIdentifier(`abc`, `cde`)}, {IdentifierType: "DatabaseObjectIdentifier", Input: `"ab.c"."cd.e"`, Expected: NewDatabaseObjectIdentifier(`ab.c`, `cd.e`)}, - {IdentifierType: "DatabaseObjectIdentifier", Input: `"a""bc"."cd""e"`, Expected: NewDatabaseObjectIdentifier(`a"bc`, `cd"e`)}, {IdentifierType: "SchemaObjectIdentifier", Input: ``, Error: "incompatible identifier: "}, {IdentifierType: "SchemaObjectIdentifier", Input: "a\nb.cde.efg", Error: "unable to read identifier: a\nb.cde.efg, err = record on line 2: wrong number of fields"}, {IdentifierType: "SchemaObjectIdentifier", Input: `a"b.cde.efg`, Error: "unable to read identifier: a\"b.cde.efg, err = parse error on line 1, column 2: bare \" in non-quoted-field"}, {IdentifierType: "SchemaObjectIdentifier", Input: `abc.cde.efg.ghi`, Error: `unexpected number of parts 4 in identifier abc.cde.efg.ghi, expected 3 in a form of ".."`}, {IdentifierType: "SchemaObjectIdentifier", Input: `abc.cde`, Error: `unexpected number of parts 2 in identifier abc.cde, expected 3 in a form of ".."`}, + {IdentifierType: "SchemaObjectIdentifier", Input: `""""."""".""""`, Error: `unable to parse identifier: """".""""."""", currently identifiers containing double quotes are not supported in the provider`}, + {IdentifierType: "SchemaObjectIdentifier", Input: `"a""bc"."cd""e"."ef""g"`, Error: `unable to parse identifier: "a""bc"."cd""e"."ef""g", currently identifiers containing double quotes are not supported in the provider`}, {IdentifierType: "SchemaObjectIdentifier", Input: `""."".""`, Expected: NewSchemaObjectIdentifier(``, ``, ``)}, - {IdentifierType: "SchemaObjectIdentifier", Input: `""""."""".""""`, Expected: NewSchemaObjectIdentifier(`"`, `"`, `"`)}, {IdentifierType: "SchemaObjectIdentifier", Input: `abc.cde.efg`, Expected: NewSchemaObjectIdentifier(`abc`, `cde`, `efg`)}, {IdentifierType: "SchemaObjectIdentifier", Input: `"abc"."cde"."efg"`, Expected: NewSchemaObjectIdentifier(`abc`, `cde`, `efg`)}, {IdentifierType: "SchemaObjectIdentifier", Input: `"ab.c"."cd.e"."ef.g"`, Expected: NewSchemaObjectIdentifier(`ab.c`, `cd.e`, `ef.g`)}, - {IdentifierType: "SchemaObjectIdentifier", Input: `"a""bc"."cd""e"."ef""g"`, Expected: NewSchemaObjectIdentifier(`a"bc`, `cd"e`, `ef"g`)}, {IdentifierType: "TableColumnIdentifier", Input: ``, Error: "incompatible identifier: "}, {IdentifierType: "TableColumnIdentifier", Input: "a\nb.cde.efg.ghi", Error: "unable to read identifier: a\nb.cde.efg.ghi, err = record on line 2: wrong number of fields"}, {IdentifierType: "TableColumnIdentifier", Input: `a"b.cde.efg.ghi`, Error: "unable to read identifier: a\"b.cde.efg.ghi, err = parse error on line 1, column 2: bare \" in non-quoted-field"}, {IdentifierType: "TableColumnIdentifier", Input: `abc.cde.efg.ghi.ijk`, Error: `unexpected number of parts 5 in identifier abc.cde.efg.ghi.ijk, expected 4 in a form of "..."`}, {IdentifierType: "TableColumnIdentifier", Input: `abc.cde`, Error: `unexpected number of parts 2 in identifier abc.cde, expected 4 in a form of "..."`}, + {IdentifierType: "TableColumnIdentifier", Input: `"""".""""."""".""""`, Error: `unable to parse identifier: """"."""".""""."""", currently identifiers containing double quotes are not supported in the provider`}, + {IdentifierType: "TableColumnIdentifier", Input: `"a""bc"."cd""e"."ef""g"."gh""i"`, Error: `unable to parse identifier: "a""bc"."cd""e"."ef""g"."gh""i", currently identifiers containing double quotes are not supported in the provider`}, {IdentifierType: "TableColumnIdentifier", Input: `"".""."".""`, Expected: NewTableColumnIdentifier(``, ``, ``, ``)}, - {IdentifierType: "TableColumnIdentifier", Input: `"""".""""."""".""""`, Expected: NewTableColumnIdentifier(`"`, `"`, `"`, `"`)}, {IdentifierType: "TableColumnIdentifier", Input: `abc.cde.efg.ghi`, Expected: NewTableColumnIdentifier(`abc`, `cde`, `efg`, `ghi`)}, {IdentifierType: "TableColumnIdentifier", Input: `"abc"."cde"."efg"."ghi"`, Expected: NewTableColumnIdentifier(`abc`, `cde`, `efg`, `ghi`)}, {IdentifierType: "TableColumnIdentifier", Input: `"ab.c"."cd.e"."ef.g"."gh.i"`, Expected: NewTableColumnIdentifier(`ab.c`, `cd.e`, `ef.g`, `gh.i`)}, - {IdentifierType: "TableColumnIdentifier", Input: `"a""bc"."cd""e"."ef""g"."gh""i"`, Expected: NewTableColumnIdentifier(`a"bc`, `cd"e`, `ef"g`, `gh"i`)}, {IdentifierType: "AccountIdentifier", Input: ``, Error: "incompatible identifier: "}, {IdentifierType: "AccountIdentifier", Input: "a\nb.cde", Error: "unable to read identifier: a\nb.cde, err = record on line 2: wrong number of fields"}, {IdentifierType: "AccountIdentifier", Input: `a"b.cde`, Error: "unable to read identifier: a\"b.cde, err = parse error on line 1, column 2: bare \" in non-quoted-field"}, {IdentifierType: "AccountIdentifier", Input: `abc.cde.efg`, Error: `unexpected number of parts 3 in identifier abc.cde.efg, expected 2 in a form of "."`}, {IdentifierType: "AccountIdentifier", Input: `abc`, Error: `unexpected number of parts 1 in identifier abc, expected 2 in a form of "."`}, + {IdentifierType: "AccountIdentifier", Input: `"""".""""`, Error: `unable to parse identifier: """"."""", currently identifiers containing double quotes are not supported in the provider`}, + {IdentifierType: "AccountIdentifier", Input: `"a""bc"."cd""e"`, Error: `unable to parse identifier: "a""bc"."cd""e", currently identifiers containing double quotes are not supported in the provider`}, {IdentifierType: "AccountIdentifier", Input: `"".""`, Expected: NewAccountIdentifier(``, ``)}, - {IdentifierType: "AccountIdentifier", Input: `"""".""""`, Expected: NewAccountIdentifier(`"`, `"`)}, {IdentifierType: "AccountIdentifier", Input: `abc.cde`, Expected: NewAccountIdentifier(`abc`, `cde`)}, {IdentifierType: "AccountIdentifier", Input: `"abc"."cde"`, Expected: NewAccountIdentifier(`abc`, `cde`)}, {IdentifierType: "AccountIdentifier", Input: `"ab.c"."cd.e"`, Expected: NewAccountIdentifier(`ab.c`, `cd.e`)}, - {IdentifierType: "AccountIdentifier", Input: `"a""bc"."cd""e"`, Expected: NewAccountIdentifier(`a"bc`, `cd"e`)}, {IdentifierType: "ExternalObjectIdentifier", Input: ``, Error: "incompatible identifier: "}, {IdentifierType: "ExternalObjectIdentifier", Input: "a\nb.cde.efg", Error: "unable to read identifier: a\nb.cde.efg, err = record on line 2: wrong number of fields"}, {IdentifierType: "ExternalObjectIdentifier", Input: `a"b.cde.efg`, Error: "unable to read identifier: a\"b.cde.efg, err = parse error on line 1, column 2: bare \" in non-quoted-field"}, {IdentifierType: "ExternalObjectIdentifier", Input: `abc.cde.efg.ghi`, Error: `unexpected number of parts 4 in identifier abc.cde.efg.ghi, expected 3 in a form of ".."`}, {IdentifierType: "ExternalObjectIdentifier", Input: `abc.cde`, Error: `unexpected number of parts 2 in identifier abc.cde, expected 3 in a form of ".."`}, + {IdentifierType: "ExternalObjectIdentifier", Input: `""""."""".""""`, Error: `unable to parse identifier: """".""""."""", currently identifiers containing double quotes are not supported in the provider`}, + {IdentifierType: "ExternalObjectIdentifier", Input: `"a""bc"."cd""e"."ef""g"`, Error: `unable to parse identifier: "a""bc"."cd""e"."ef""g", currently identifiers containing double quotes are not supported in the provider`}, {IdentifierType: "ExternalObjectIdentifier", Input: `""."".""`, Expected: NewExternalObjectIdentifier(NewAccountIdentifier(``, ``), NewAccountObjectIdentifier(``))}, - {IdentifierType: "ExternalObjectIdentifier", Input: `""""."""".""""`, Expected: NewExternalObjectIdentifier(NewAccountIdentifier(`"`, `"`), NewAccountObjectIdentifier(`"`))}, {IdentifierType: "ExternalObjectIdentifier", Input: `abc.cde.efg`, Expected: NewExternalObjectIdentifier(NewAccountIdentifier(`abc`, `cde`), NewAccountObjectIdentifier(`efg`))}, {IdentifierType: "ExternalObjectIdentifier", Input: `"abc"."cde"."efg"`, Expected: NewExternalObjectIdentifier(NewAccountIdentifier(`abc`, `cde`), NewAccountObjectIdentifier(`efg`))}, {IdentifierType: "ExternalObjectIdentifier", Input: `"ab.c"."cd.e"."ef.g"`, Expected: NewExternalObjectIdentifier(NewAccountIdentifier(`ab.c`, `cd.e`), NewAccountObjectIdentifier(`ef.g`))}, - {IdentifierType: "ExternalObjectIdentifier", Input: `"a""bc"."cd""e"."ef""g"`, Expected: NewExternalObjectIdentifier(NewAccountIdentifier(`a"bc`, `cd"e`), NewAccountObjectIdentifier(`ef"g`))}, } for _, testCase := range testCases { @@ -238,6 +238,8 @@ func Test_ParseObjectIdentifierString(t *testing.T) { {Input: `abc`, Expected: NewAccountObjectIdentifier(`abc`)}, {Input: `abc.def`, Expected: NewDatabaseObjectIdentifier(`abc`, `def`)}, {Input: `abc.def.ghi`, Expected: NewSchemaObjectIdentifier(`abc`, `def`, `ghi`)}, + {Input: `abc."d.e.f".ghi`, Expected: NewSchemaObjectIdentifier(`abc`, `d.e.f`, `ghi`)}, + {Input: `abc."d""e""f".ghi`, Expected: NewSchemaObjectIdentifier(`abc`, `d"e"f`, `ghi`), Error: `unable to parse identifier: abc."d""e""f".ghi, currently identifiers containing double quotes are not supported in the provider`}, {Input: `abc.def.ghi.jkl`, Expected: NewTableColumnIdentifier(`abc`, `def`, `ghi`, `jkl`)}, } diff --git a/pkg/sdk/testint/identifier_integration_test.go b/pkg/sdk/testint/identifier_integration_test.go new file mode 100644 index 0000000000..f4153a5252 --- /dev/null +++ b/pkg/sdk/testint/identifier_integration_test.go @@ -0,0 +1,163 @@ +package testint + +import ( + "context" + "fmt" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInt_IdentifiersForOnePartIdentifierAsNameAndReference(t *testing.T) { + testCases := []struct { + Name sdk.AccountObjectIdentifier + ShowName string + Error string + }{ + // special cases + {Name: sdk.NewAccountObjectIdentifier(``), Error: "invalid object identifier"}, + {Name: sdk.NewAccountObjectIdentifier(`"`), Error: "invalid object identifier"}, + // This is a valid identifier, but because in NewXIdentifier functions we're trimming double quotes it won't work + {Name: sdk.NewAccountObjectIdentifier(`""`), Error: "invalid object identifier"}, + // This is a valid identifier, but because in NewXIdentifier functions we're trimming double quotes it won't work + {Name: sdk.NewAccountObjectIdentifier(`""""`), Error: "invalid object identifier"}, + {Name: sdk.NewAccountObjectIdentifier(`"."`), ShowName: `.`}, + + // lower case + {Name: sdk.NewAccountObjectIdentifier(`abc`), ShowName: `abc`}, + {Name: sdk.NewAccountObjectIdentifier(`ab.c`), ShowName: `ab.c`}, + {Name: sdk.NewAccountObjectIdentifier(`a"bc`), Error: `unexpected '"`}, + {Name: sdk.NewAccountObjectIdentifier(`"a""bc"`), ShowName: `a"bc`}, + + // upper case + {Name: sdk.NewAccountObjectIdentifier(`ABC`), ShowName: `ABC`}, + {Name: sdk.NewAccountObjectIdentifier(`AB.C`), ShowName: `AB.C`}, + {Name: sdk.NewAccountObjectIdentifier(`A"BC`), Error: `unexpected '"`}, + {Name: sdk.NewAccountObjectIdentifier(`"A""BC"`), ShowName: `A"BC`}, + + // mixed case + {Name: sdk.NewAccountObjectIdentifier(`AbC`), ShowName: `AbC`}, + {Name: sdk.NewAccountObjectIdentifier(`Ab.C`), ShowName: `Ab.C`}, + {Name: sdk.NewAccountObjectIdentifier(`A"bC`), Error: `unexpected '"`}, + {Name: sdk.NewAccountObjectIdentifier(`"A""bC"`), ShowName: `A"bC`}, + } + + for _, testCase := range testCases { + testCase := testCase + + t.Run(fmt.Sprintf("one part identifier name and reference for input: %s", testCase.Name.FullyQualifiedName()), func(t *testing.T) { + ctx := context.Background() + + err := testClient(t).ResourceMonitors.Create(ctx, testCase.Name, new(sdk.CreateResourceMonitorOptions)) + if testCase.Error != "" { + require.ErrorContains(t, err, testCase.Error) + } else { + t.Cleanup(testClientHelper().ResourceMonitor.DropResourceMonitorFunc(t, testCase.Name)) + } + + err = testClient(t).Warehouses.Create(ctx, testCase.Name, &sdk.CreateWarehouseOptions{ + ResourceMonitor: &testCase.Name, + }) + if testCase.Error != "" { + require.ErrorContains(t, err, testCase.Error) + } else { + require.NoError(t, err) + t.Cleanup(testClientHelper().Warehouse.DropWarehouseFunc(t, testCase.Name)) + var result struct { + Name string `db:"name"` + ResourceMonitor string `db:"resource_monitor"` + } + err = testClient(t).QueryOneForTests(ctx, &result, fmt.Sprintf("SHOW WAREHOUSES LIKE '%s'", testCase.ShowName)) + require.NoError(t, err) + + // For one part identifiers, we expect Snowflake to return unescaped identifiers (just like the ones we used for SHOW) + assert.Equal(t, testCase.ShowName, result.Name) + assert.Equal(t, testCase.ShowName, result.ResourceMonitor) + } + }) + } +} + +func TestInt_IdentifiersForTwoPartIdentifierAsReference(t *testing.T) { + type RawGrantOutput struct { + Name string `db:"name"` + Privilege string `db:"privilege"` + } + + testCases := []struct { + Name sdk.DatabaseObjectIdentifier + OverrideExpectedSnowflakeOutput string + Error string + }{ + // special cases + {Name: sdk.NewDatabaseObjectIdentifier(``, ``), Error: "invalid object identifier"}, + {Name: sdk.NewDatabaseObjectIdentifier(`"`, `"`), Error: "invalid object identifier"}, + // This is a valid identifier, but because in NewXIdentifier functions we're trimming double quotes it won't work + {Name: sdk.NewDatabaseObjectIdentifier(`""`, `""`), Error: "invalid object identifier"}, + // This is a valid identifier, but because in NewXIdentifier functions we're trimming double quotes it won't work + {Name: sdk.NewDatabaseObjectIdentifier(`""""`, `""""`), Error: "invalid object identifier"}, + {Name: sdk.NewDatabaseObjectIdentifier(`"."`, `"."`)}, + + // lower case + {Name: sdk.NewDatabaseObjectIdentifier(`abc`, `abc`)}, + {Name: sdk.NewDatabaseObjectIdentifier(`ab.c`, `ab.c`)}, + {Name: sdk.NewDatabaseObjectIdentifier(`a"bc`, `a"bc`), Error: `unexpected '"`}, + {Name: sdk.NewDatabaseObjectIdentifier(`"a""bc"`, `"a""bc"`)}, + + // upper case + {Name: sdk.NewDatabaseObjectIdentifier(`ABC`, `ABC`), OverrideExpectedSnowflakeOutput: `ABC.ABC`}, + {Name: sdk.NewDatabaseObjectIdentifier(`AB.C`, `AB.C`)}, + {Name: sdk.NewDatabaseObjectIdentifier(`A"BC`, `A"BC`), Error: `unexpected '"`}, + {Name: sdk.NewDatabaseObjectIdentifier(`"A""BC"`, `"A""BC"`)}, + + // mixed case + {Name: sdk.NewDatabaseObjectIdentifier(`AbC`, `AbC`)}, + {Name: sdk.NewDatabaseObjectIdentifier(`Ab.C`, `Ab.C`)}, + {Name: sdk.NewDatabaseObjectIdentifier(`A"bC`, `A"bC`), Error: `unexpected '"`}, + {Name: sdk.NewDatabaseObjectIdentifier(`"A""bC"`, `"A""bC"`)}, + } + + role, roleCleanup := testClientHelper().Role.CreateRole(t) + t.Cleanup(roleCleanup) + + for _, testCase := range testCases { + t.Run(fmt.Sprintf("two part identifier reference for input: %s", testCase.Name.FullyQualifiedName()), func(t *testing.T) { + ctx := context.Background() + + err := testClient(t).Databases.Create(ctx, testCase.Name.DatabaseId(), new(sdk.CreateDatabaseOptions)) + if testCase.Error != "" { + require.ErrorContains(t, err, testCase.Error) + } else { + t.Cleanup(testClientHelper().Database.DropDatabaseFunc(t, testCase.Name.DatabaseId())) + } + + err = testClient(t).Schemas.Create(ctx, testCase.Name, new(sdk.CreateSchemaOptions)) + if testCase.Error != "" { + require.ErrorContains(t, err, testCase.Error) + } else { + require.NoError(t, err) + t.Cleanup(testClientHelper().Schema.DropSchemaFunc(t, testCase.Name)) + + testClientHelper().Grant.GrantOnSchemaToAccountRole(t, testCase.Name, role.ID(), sdk.SchemaPrivilegeCreateTable) + + var grants []RawGrantOutput + err = testClient(t).QueryForTests(ctx, &grants, fmt.Sprintf("SHOW GRANTS ON SCHEMA %s", testCase.Name.FullyQualifiedName())) + require.NoError(t, err) + + createTableGrant, err := collections.FindOne(grants, func(output RawGrantOutput) bool { return output.Privilege == sdk.SchemaPrivilegeCreateTable.String() }) + require.NoError(t, err) + + // For two part identifiers, we expect Snowflake to return escaped identifiers with exception + // to identifiers that don't have any lowercase character and special symbol in it. + if testCase.OverrideExpectedSnowflakeOutput != "" { + assert.Equal(t, testCase.OverrideExpectedSnowflakeOutput, createTableGrant.Name) + } else { + assert.Equal(t, testCase.Name.FullyQualifiedName(), createTableGrant.Name) + } + } + }) + } +} diff --git a/pkg/sdk/testint/identifier_parsers_integration_test.go b/pkg/sdk/testint/identifier_parsers_integration_test.go deleted file mode 100644 index 6f54e08e64..0000000000 --- a/pkg/sdk/testint/identifier_parsers_integration_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package testint - -import ( - "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "testing" -) - -// Upper case -// case a"b in a single field and in e.g. grant field where it's encoded - -func TestInt_IdentifierParsing(t *testing.T) { - testCases := []struct { - InputName string - }{} - - testClientHelper().NetworkPolicy.CreateNetworkPolicyWithRequest(t, sdk.NewCreateNetworkPolicyRequest()) - -}