package backup_test

import (
	"database/sql"
	"fmt"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/apache/cloudberry-backup/backup"
	"github.com/apache/cloudberry-backup/testutils"
	"github.com/apache/cloudberry-backup/toc"
	"github.com/apache/cloudberry-go-libs/structmatcher"
	"github.com/apache/cloudberry-go-libs/testhelper"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("backup/predata_acl tests", func() {
	BeforeEach(func() {
		tocfile, backupfile = testutils.InitializeTestTOC(buffer, "predata")
	})
	Describe("PrintObjectMetadata", func() {
		table := backup.Table{Relation: backup.Relation{Schema: "public", Name: "tablename"}}
		hasAllPrivileges := testutils.DefaultACLForType("anothertestrole", toc.OBJ_TABLE)
		hasMostPrivileges := testutils.DefaultACLForType("testrole", toc.OBJ_TABLE)
		hasMostPrivileges.Trigger = false
		hasSinglePrivilege := backup.ACL{Grantee: "", Trigger: true}
		hasAllPrivilegesWithGrant := testutils.DefaultACLForTypeWithGrant("anothertestrole", toc.OBJ_TABLE)
		hasMostPrivilegesWithGrant := testutils.DefaultACLForTypeWithGrant("testrole", toc.OBJ_TABLE)
		hasMostPrivilegesWithGrant.TriggerWithGrant = false
		hasSinglePrivilegeWithGrant := backup.ACL{Grantee: "", TriggerWithGrant: true}
		privileges := []backup.ACL{hasAllPrivileges, hasMostPrivileges, hasSinglePrivilege}
		privilegesWithGrant := []backup.ACL{hasAllPrivilegesWithGrant, hasMostPrivilegesWithGrant, hasSinglePrivilegeWithGrant}
		It("prints a block with a table comment", func() {
			tableMetadata := backup.ObjectMetadata{Comment: "This is a table comment."}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

COMMENT ON TABLE public.tablename IS 'This is a table comment.';`)
		})
		It("prints a block with a table comment with special characters", func() {
			tableMetadata := backup.ObjectMetadata{Comment: `This is a ta'ble 1+=;,./\>,<@\\n^comment.`}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

COMMENT ON TABLE public.tablename IS 'This is a ta''ble 1+=;,./\>,<@\\n^comment.';`)
		})
		It("prints an ALTER TABLE ... OWNER TO statement to set the table owner", func() {
			tableMetadata := backup.ObjectMetadata{Owner: "testrole"}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

ALTER TABLE public.tablename OWNER TO testrole;`)
		})
		It("prints a block of REVOKE and GRANT statements", func() {
			tableMetadata := backup.ObjectMetadata{Privileges: privileges}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

REVOKE ALL ON TABLE public.tablename FROM PUBLIC;
GRANT ALL ON TABLE public.tablename TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.tablename TO testrole;
GRANT TRIGGER ON TABLE public.tablename TO PUBLIC;`)
		})
		It("prints a block of REVOKE and GRANT statements WITH GRANT OPTION", func() {
			tableMetadata := backup.ObjectMetadata{Privileges: privilegesWithGrant}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

REVOKE ALL ON TABLE public.tablename FROM PUBLIC;
GRANT ALL ON TABLE public.tablename TO anothertestrole WITH GRANT OPTION;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.tablename TO testrole WITH GRANT OPTION;
GRANT TRIGGER ON TABLE public.tablename TO PUBLIC WITH GRANT OPTION;`)
		})
		It("prints a block of REVOKE and GRANT statements, some with WITH GRANT OPTION, some without", func() {
			tableMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{hasAllPrivileges, hasMostPrivilegesWithGrant}}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

REVOKE ALL ON TABLE public.tablename FROM PUBLIC;
GRANT ALL ON TABLE public.tablename TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.tablename TO testrole WITH GRANT OPTION;`)
		})
		It("prints both an ALTER TABLE ... OWNER TO statement and a table comment", func() {
			tableMetadata := backup.ObjectMetadata{Comment: "This is a table comment.", Owner: "testrole"}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

COMMENT ON TABLE public.tablename IS 'This is a table comment.';


ALTER TABLE public.tablename OWNER TO testrole;`)
		})
		It("prints both a block of REVOKE and GRANT statements and an ALTER TABLE ... OWNER TO statement", func() {
			tableMetadata := backup.ObjectMetadata{Privileges: privileges, Owner: "testrole"}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

ALTER TABLE public.tablename OWNER TO testrole;


REVOKE ALL ON TABLE public.tablename FROM PUBLIC;
REVOKE ALL ON TABLE public.tablename FROM testrole;
GRANT ALL ON TABLE public.tablename TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.tablename TO testrole;
GRANT TRIGGER ON TABLE public.tablename TO PUBLIC;`)
		})
		It("prints both a block of REVOKE and GRANT statements and a table comment", func() {
			tableMetadata := backup.ObjectMetadata{Privileges: privileges, Comment: "This is a table comment."}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

COMMENT ON TABLE public.tablename IS 'This is a table comment.';


REVOKE ALL ON TABLE public.tablename FROM PUBLIC;
GRANT ALL ON TABLE public.tablename TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.tablename TO testrole;
GRANT TRIGGER ON TABLE public.tablename TO PUBLIC;`)
		})
		It("prints REVOKE and GRANT statements, an ALTER TABLE ... OWNER TO statement, and comments", func() {
			tableMetadata := backup.ObjectMetadata{Privileges: privileges, Owner: "testrole", Comment: "This is a table comment."}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

COMMENT ON TABLE public.tablename IS 'This is a table comment.';


ALTER TABLE public.tablename OWNER TO testrole;


REVOKE ALL ON TABLE public.tablename FROM PUBLIC;
REVOKE ALL ON TABLE public.tablename FROM testrole;
GRANT ALL ON TABLE public.tablename TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.tablename TO testrole;
GRANT TRIGGER ON TABLE public.tablename TO PUBLIC;`)
		})
		It("prints SERVER for ALTER and FOREIGN SERVER for GRANT/REVOKE for a foreign server", func() {
			server := backup.ForeignServer{Name: "foreignserver"}
			serverPrivileges := testutils.DefaultACLForType("testrole", toc.OBJ_FOREIGN_SERVER)
			serverMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{serverPrivileges}, Owner: "testrole"}
			backup.PrintObjectMetadata(backupfile, tocfile, serverMetadata, server, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

ALTER SERVER foreignserver OWNER TO testrole;


REVOKE ALL ON FOREIGN SERVER foreignserver FROM PUBLIC;
REVOKE ALL ON FOREIGN SERVER foreignserver FROM testrole;
GRANT ALL ON FOREIGN SERVER foreignserver TO testrole;`)
		})
		It("prints FUNCTION for GRANT/REVOKE and AGGREGATE for ALTER for an aggregate function", func() {
			aggregate := backup.Aggregate{Schema: "public", Name: "testagg"}
			aggregatePrivileges := testutils.DefaultACLForType("testrole", toc.OBJ_AGGREGATE)
			aggregateMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{aggregatePrivileges}, Owner: "testrole"}
			backup.PrintObjectMetadata(backupfile, tocfile, aggregateMetadata, aggregate, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

ALTER AGGREGATE public.testagg(*) OWNER TO testrole;


REVOKE ALL ON FUNCTION public.testagg(*) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.testagg(*) FROM testrole;
GRANT ALL ON FUNCTION public.testagg(*) TO testrole;`)
		})
		It("prints TABLE for a block of REVOKE and GRANT statements for a foreign table", func() {
			table := backup.Table{Relation: backup.Relation{Schema: "public", Name: "foreigntablename"}}
			foreignDef := backup.ForeignTableDefinition{Oid: 23, Options: "", Server: "fs"}
			table.ForeignDef = foreignDef
			tableMetadata := backup.ObjectMetadata{Privileges: privileges}
			backup.PrintObjectMetadata(backupfile, tocfile, tableMetadata, table, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

REVOKE ALL ON TABLE public.foreigntablename FROM PUBLIC;
GRANT ALL ON TABLE public.foreigntablename TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.foreigntablename TO testrole;
GRANT TRIGGER ON TABLE public.foreigntablename TO PUBLIC;`)
		})
		It("prints TABLE for a block of REVOKE and GRANT statements for a view", func() {
			view := backup.View{Schema: "public", Name: "viewname"}
			viewMetadata := backup.ObjectMetadata{Privileges: privileges}
			backup.PrintObjectMetadata(backupfile, tocfile, viewMetadata, view, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

REVOKE ALL ON TABLE public.viewname FROM PUBLIC;
GRANT ALL ON TABLE public.viewname TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.viewname TO testrole;
GRANT TRIGGER ON TABLE public.viewname TO PUBLIC;`)
		})
		It("prints TABLE for a block of REVOKE and GRANT statements for a materialized view", func() {
			view := backup.View{Schema: "public", Name: "matviewname", IsMaterialized: true}
			viewMetadata := backup.ObjectMetadata{Privileges: privileges}
			backup.PrintObjectMetadata(backupfile, tocfile, viewMetadata, view, "", []uint32{0, 0})
			testhelper.ExpectRegexp(buffer, `

REVOKE ALL ON TABLE public.matviewname FROM PUBLIC;
GRANT ALL ON TABLE public.matviewname TO anothertestrole;
GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES ON TABLE public.matviewname TO testrole;
GRANT TRIGGER ON TABLE public.matviewname TO PUBLIC;`)
		})
		Context("Views and sequences have owners", func() {
			view := backup.View{Schema: "public", Name: "viewname"}
			sequence := backup.Sequence{Relation: backup.Relation{Schema: "public", Name: "sequencename"}}
			objectMetadata := backup.ObjectMetadata{Owner: "testrole"}

			It("prints an ALTER TABLE ... OWNER TO statement to set the owner for a sequence", func() {
				expectedKeyword := `TABLE`
				if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() {
					expectedKeyword = `SEQUENCE`
				}

				backup.PrintObjectMetadata(backupfile, tocfile, objectMetadata, sequence, "public.sequencename", []uint32{0, 0})
				testhelper.ExpectRegexp(buffer, fmt.Sprintf(`

ALTER %s public.sequencename OWNER TO testrole;`, expectedKeyword))
			})
			It("prints an ALTER TABLE ... OWNER TO statement to set the owner for a view", func() {
				expectedKeyword := `TABLE`
				if (connectionPool.Version.IsGPDB() && connectionPool.Version.AtLeast("6")) || connectionPool.Version.IsCBDB() {
					expectedKeyword = `VIEW`
				}

				backup.PrintObjectMetadata(backupfile, tocfile, objectMetadata, view, "public.viewname", []uint32{0, 0})
				testhelper.ExpectRegexp(buffer, fmt.Sprintf(`

ALTER %s public.viewname OWNER TO testrole;`, expectedKeyword))
			})
		})
	})
	Describe("PrintDefaultPrivilegeStatements", func() {
		privs := []backup.ACL{{Grantee: "", Usage: true}}
		It("prints ALTER DEFAULT PRIVILEGES statement for relation", func() {
			defaultPrivileges := []backup.DefaultPrivileges{{Owner: "testrole", Schema: "", Privileges: privs, ObjectType: "r"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON TABLES FROM testrole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole GRANT USAGE ON TABLES TO PUBLIC;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement for sequence", func() {
			defaultPrivileges := []backup.DefaultPrivileges{{Owner: "testrole", Schema: "", Privileges: privs, ObjectType: "S"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON SEQUENCES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON SEQUENCES FROM testrole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole GRANT USAGE ON SEQUENCES TO PUBLIC;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement for function", func() {
			defaultPrivileges := []backup.DefaultPrivileges{{Owner: "testrole", Schema: "", Privileges: privs, ObjectType: "f"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON FUNCTIONS FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON FUNCTIONS FROM testrole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole GRANT USAGE ON FUNCTIONS TO PUBLIC;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement for type", func() {
			defaultPrivileges := []backup.DefaultPrivileges{{Owner: "testrole", Schema: "", Privileges: privs, ObjectType: "T"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON TYPES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON TYPES FROM testrole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole GRANT ALL ON TYPES TO PUBLIC;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement for role", func() {
			localPrivs := []backup.ACL{{Grantee: "somerole", Usage: true}}
			defaultPrivileges := []backup.DefaultPrivileges{{Schema: "", Owner: "somerole", Privileges: localPrivs, ObjectType: "r"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE somerole REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE somerole REVOKE ALL ON TABLES FROM somerole;
ALTER DEFAULT PRIVILEGES FOR ROLE somerole GRANT USAGE ON TABLES TO somerole;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement in schema", func() {
			defaultPrivileges := []backup.DefaultPrivileges{{Owner: "testrole", Schema: "myschema", Privileges: privs, ObjectType: "r"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE testrole IN SCHEMA myschema REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole IN SCHEMA myschema REVOKE ALL ON TABLES FROM testrole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole IN SCHEMA myschema GRANT USAGE ON TABLES TO PUBLIC;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement for role in schema", func() {
			localPrivs := []backup.ACL{{Grantee: "somerole", Usage: true}}
			defaultPrivileges := []backup.DefaultPrivileges{{Schema: "myschema", Owner: "somerole", Privileges: localPrivs, ObjectType: "r"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE somerole IN SCHEMA myschema REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE somerole IN SCHEMA myschema REVOKE ALL ON TABLES FROM somerole;
ALTER DEFAULT PRIVILEGES FOR ROLE somerole IN SCHEMA myschema GRANT USAGE ON TABLES TO somerole;
`)
		})
		It("prints ALTER DEFAULT PRIVILEGES statement with grant option", func() {
			localPrivs := []backup.ACL{{Grantee: "somerole", Usage: true, UsageWithGrant: true}}
			defaultPrivileges := []backup.DefaultPrivileges{{Owner: "testrole", Schema: "", Privileges: localPrivs, ObjectType: "r"}}
			backup.PrintDefaultPrivilegesStatements(backupfile, tocfile, defaultPrivileges)
			testhelper.ExpectRegexp(buffer, `
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole REVOKE ALL ON TABLES FROM testrole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole GRANT USAGE ON TABLES TO somerole;
ALTER DEFAULT PRIVILEGES FOR ROLE testrole GRANT USAGE ON TABLES TO somerole WITH GRANT OPTION;
`)
		})
	})
	Describe("ConstructMetadataMap", func() {
		object1A := backup.MetadataQueryStruct{UniqueID: backup.UniqueID{Oid: 1}, Privileges: sql.NullString{String: "gpadmin=r/gpadmin", Valid: true}, Kind: "", Owner: "testrole", Comment: ""}
		object1B := backup.MetadataQueryStruct{UniqueID: backup.UniqueID{Oid: 1}, Privileges: sql.NullString{String: "testrole=r/testrole", Valid: true}, Kind: "", Owner: "testrole", Comment: ""}
		object2 := backup.MetadataQueryStruct{UniqueID: backup.UniqueID{Oid: 2}, Privileges: sql.NullString{String: "testrole=r/testrole", Valid: true}, Kind: "", Owner: "testrole", Comment: "this is a comment", SecurityLabelProvider: "some_provider", SecurityLabel: "some_label"}
		objectDefaultKind := backup.MetadataQueryStruct{UniqueID: backup.UniqueID{Oid: 3}, Privileges: sql.NullString{String: "", Valid: false}, Kind: "Default", Owner: "testrole", Comment: ""}
		objectEmptyKind := backup.MetadataQueryStruct{UniqueID: backup.UniqueID{Oid: 4}, Privileges: sql.NullString{String: "", Valid: false}, Kind: "Empty", Owner: "testrole", Comment: ""}
		var metadataList []backup.MetadataQueryStruct
		BeforeEach(func() {
			rolnames := sqlmock.NewRows([]string{"rolename", "quotedrolename"}).
				AddRow("gpadmin", "gpadmin").
				AddRow("testrole", "testrole")
			mock.ExpectQuery("SELECT rolname (.*)").
				WillReturnRows(rolnames)
			metadataList = []backup.MetadataQueryStruct{}
		})
		It("No objects", func() {
			metadataMap := backup.ConstructMetadataMap(metadataList)
			Expect(metadataMap).To(BeEmpty())
		})
		It("One object", func() {
			metadataList = []backup.MetadataQueryStruct{object2}
			metadataMap := backup.ConstructMetadataMap(metadataList)
			expectedObjectMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{{Grantee: "testrole", Select: true}}, Owner: "testrole", Comment: "this is a comment", SecurityLabelProvider: "some_provider", SecurityLabel: "some_label"}
			Expect(metadataMap).To(HaveLen(1))
			Expect(metadataMap[backup.UniqueID{Oid: 2}]).To(Equal(expectedObjectMetadata))

		})
		It("One object with two ACL entries", func() {
			metadataList = []backup.MetadataQueryStruct{object1A, object1B}
			metadataMap := backup.ConstructMetadataMap(metadataList)
			expectedObjectMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{{Grantee: "gpadmin", Select: true}, {Grantee: "testrole", Select: true}}, Owner: "testrole"}
			Expect(metadataMap).To(HaveLen(1))
			Expect(metadataMap[backup.UniqueID{Oid: 1}]).To(Equal(expectedObjectMetadata))
		})
		It("Multiple objects", func() {
			metadataList = []backup.MetadataQueryStruct{object1A, object1B, object2}
			metadataMap := backup.ConstructMetadataMap(metadataList)
			expectedObjectMetadataOne := backup.ObjectMetadata{Privileges: []backup.ACL{{Grantee: "gpadmin", Select: true}, {Grantee: "testrole", Select: true}}, Owner: "testrole"}
			expectedObjectMetadataTwo := backup.ObjectMetadata{Privileges: []backup.ACL{{Grantee: "testrole", Select: true}}, Owner: "testrole", Comment: "this is a comment", SecurityLabelProvider: "some_provider", SecurityLabel: "some_label"}
			Expect(metadataMap).To(HaveLen(2))
			Expect(metadataMap[backup.UniqueID{Oid: 1}]).To(Equal(expectedObjectMetadataOne))
			Expect(metadataMap[backup.UniqueID{Oid: 2}]).To(Equal(expectedObjectMetadataTwo))
		})
		It("Default Kind", func() {
			metadataList = []backup.MetadataQueryStruct{objectDefaultKind}
			metadataMap := backup.ConstructMetadataMap(metadataList)
			expectedObjectMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{}, Owner: "testrole"}
			Expect(metadataMap).To(HaveLen(1))
			Expect(metadataMap[backup.UniqueID{Oid: 3}]).To(Equal(expectedObjectMetadata))
		})
		It("'Empty' Kind", func() {
			metadataList = []backup.MetadataQueryStruct{objectEmptyKind}
			metadataMap := backup.ConstructMetadataMap(metadataList)
			expectedObjectMetadata := backup.ObjectMetadata{Privileges: []backup.ACL{{Grantee: "GRANTEE"}}, Owner: "testrole"}
			Expect(metadataMap).To(HaveLen(1))
			Expect(metadataMap[backup.UniqueID{Oid: 4}]).To(Equal(expectedObjectMetadata))
		})
	})
	Describe("ParseACL", func() {
		var quotedRoleNames map[string]string
		BeforeEach(func() {
			quotedRoleNames = map[string]string{
				"testrole":  "testrole",
				"Test|role": `"Test|role"`,
			}
			backup.SetQuotedRoleNames(quotedRoleNames)
		})
		It("parses an ACL string representing default privileges", func() {
			aclStr := ""
			result := backup.ParseACL(aclStr)
			Expect(result).To(BeNil())
		})
		It("parses an ACL string representing no privileges", func() {
			aclStr := "GRANTEE=/GRANTOR"
			expected := backup.ACL{Grantee: "GRANTEE"}
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
		It("parses an ACL string containing a role with multiple privileges", func() {
			aclStr := "testrole=arwdDxt/gpadmin"
			expected := testutils.DefaultACLForType("testrole", toc.OBJ_TABLE)
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
		It("parses an ACL string containing a role with one privilege", func() {
			aclStr := "testrole=a/gpadmin"
			expected := backup.ACL{Grantee: "testrole", Insert: true}
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
		It("parses an ACL string containing a role name with special characters", func() {
			aclStr := `Test|role=a/gpadmin`
			expected := backup.ACL{Grantee: `"Test|role"`, Insert: true}
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
		It("parses an ACL string containing a role with some privileges with GRANT and some without including GRANT", func() {
			aclStr := "testrole=ar*w*d*tXUCTc/gpadmin"
			expected := backup.ACL{Grantee: "testrole", Insert: true, SelectWithGrant: true, UpdateWithGrant: true,
				DeleteWithGrant: true, Trigger: true, Execute: true, Usage: true, Create: true, Temporary: true, Connect: true}
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
		It("parses an ACL string containing a role with all privileges including GRANT", func() {
			aclStr := "testrole=a*D*x*t*X*U*C*T*c*/gpadmin"
			expected := backup.ACL{Grantee: "testrole", InsertWithGrant: true, TruncateWithGrant: true, ReferencesWithGrant: true,
				TriggerWithGrant: true, ExecuteWithGrant: true, UsageWithGrant: true, CreateWithGrant: true, TemporaryWithGrant: true, ConnectWithGrant: true}
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
		It("parses an ACL string granting privileges to PUBLIC", func() {
			aclStr := "=a/gpadmin"
			expected := backup.ACL{Grantee: "", Insert: true}
			result := backup.ParseACL(aclStr)
			structmatcher.ExpectStructsToMatch(&expected, result)
		})
	})
	Describe("ConstructDefaultPrivileges", func() {
		object1A := backup.DefaultPrivilegesQueryStruct{Oid: 1, Owner: "testrole", Schema: "myschema", Kind: "", ObjectType: "r", Privileges: sql.NullString{String: "gpadmin=r/gpadmin", Valid: true}}
		object1B := backup.DefaultPrivilegesQueryStruct{Oid: 1, Owner: "testrole", Schema: "myschema", Kind: "", ObjectType: "r", Privileges: sql.NullString{String: "testrole=r/testrole", Valid: true}}
		object2 := backup.DefaultPrivilegesQueryStruct{Oid: 2, Owner: "testrole", Schema: "myschema", Kind: "", ObjectType: "S", Privileges: sql.NullString{String: "testrole=r/testrole", Valid: true}}
		objectDefaultKind := backup.DefaultPrivilegesQueryStruct{Oid: 3, Owner: "testrole", Schema: "", Kind: "Default", ObjectType: "T", Privileges: sql.NullString{String: "", Valid: false}}
		objectEmptyKind := backup.DefaultPrivilegesQueryStruct{Oid: 4, Owner: "testrole", Schema: "", Kind: "Empty", ObjectType: "f", Privileges: sql.NullString{String: "", Valid: false}}
		var privilegesQuerylist []backup.DefaultPrivilegesQueryStruct
		BeforeEach(func() {
			rolnames := sqlmock.NewRows([]string{"rolename", "quotedrolename"}).
				AddRow("gpadmin", "gpadmin").
				AddRow("testrole", "testrole")
			mock.ExpectQuery("SELECT rolname (.*)").
				WillReturnRows(rolnames)
			privilegesQuerylist = []backup.DefaultPrivilegesQueryStruct{}
		})
		It("returns no privileges when no default privileges exist", func() {
			privilegesList := backup.ConstructDefaultPrivileges(privilegesQuerylist)
			Expect(privilegesList).To(BeEmpty())
		})
		It("constructs a single sequence default privilege in a specific schema", func() {
			privilegesQuerylist = []backup.DefaultPrivilegesQueryStruct{object2}
			privilegesList := backup.ConstructDefaultPrivileges(privilegesQuerylist)
			expectedDefaultPrivileges := backup.DefaultPrivileges{Privileges: []backup.ACL{{Grantee: "testrole", Select: true}}, Owner: "testrole", Schema: "myschema", ObjectType: "S"}
			Expect(privilegesList).To(HaveLen(1))
			Expect(privilegesList[0]).To(Equal(expectedDefaultPrivileges))
		})
		It("constructs multiple default privileges on a single relation in a specific schema", func() {
			privilegesQuerylist = []backup.DefaultPrivilegesQueryStruct{object1A, object1B}
			privilegesList := backup.ConstructDefaultPrivileges(privilegesQuerylist)
			expectedObjectMetadata := backup.DefaultPrivileges{Privileges: []backup.ACL{{Grantee: "gpadmin", Select: true}, {Grantee: "testrole", Select: true}}, Owner: "testrole", Schema: "myschema", ObjectType: "r"}
			Expect(privilegesList).To(HaveLen(1))
			Expect(privilegesList[0]).To(Equal(expectedObjectMetadata))
		})
		It("constructs multiple default privileges on multiple objects in a specific schema", func() {
			privilegesQuerylist = []backup.DefaultPrivilegesQueryStruct{object1A, object1B, object2}
			privilegesList := backup.ConstructDefaultPrivileges(privilegesQuerylist)
			expectedObjectMetadataOne := backup.DefaultPrivileges{Privileges: []backup.ACL{{Grantee: "gpadmin", Select: true}, {Grantee: "testrole", Select: true}}, Owner: "testrole", Schema: "myschema", ObjectType: "r"}
			expectedObjectMetadataTwo := backup.DefaultPrivileges{Privileges: []backup.ACL{{Grantee: "testrole", Select: true}}, Owner: "testrole", Schema: "myschema", ObjectType: "S"}
			Expect(privilegesList).To(HaveLen(2))
			Expect(privilegesList[0]).To(Equal(expectedObjectMetadataOne))
			Expect(privilegesList[1]).To(Equal(expectedObjectMetadataTwo))
		})
		It("constructs a default privilege for a type with a 'Default' kind", func() {
			privilegesQuerylist = []backup.DefaultPrivilegesQueryStruct{objectDefaultKind}
			privilegesList := backup.ConstructDefaultPrivileges(privilegesQuerylist)
			expectedObjectMetadata := backup.DefaultPrivileges{Privileges: []backup.ACL{}, Owner: "testrole", Schema: "", ObjectType: "T"}
			Expect(privilegesList).To(HaveLen(1))
			Expect(privilegesList[0]).To(Equal(expectedObjectMetadata))
		})
		It("constructs a default privilege for a function with an 'Empty' kind", func() {
			privilegesQuerylist = []backup.DefaultPrivilegesQueryStruct{objectEmptyKind}
			privilegesList := backup.ConstructDefaultPrivileges(privilegesQuerylist)
			expectedObjectMetadata := backup.DefaultPrivileges{Privileges: []backup.ACL{{Grantee: "GRANTEE"}}, Owner: "testrole", Schema: "", ObjectType: "f"}
			Expect(privilegesList).To(HaveLen(1))
			Expect(privilegesList[0]).To(Equal(expectedObjectMetadata))
		})
	})
})
