package backup_test

import (
	"fmt"

	"github.com/apache/cloudberry-backup/backup"
	"github.com/apache/cloudberry-backup/testutils"
	"github.com/apache/cloudberry-backup/toc"
	"github.com/apache/cloudberry-go-libs/testhelper"

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

var _ = Describe("backup/metadata_globals tests", func() {
	emptyDB := backup.Database{}
	BeforeEach(func() {
		tocfile, backupfile = testutils.InitializeTestTOC(buffer, "global")
	})
	Describe("PrintSessionGUCs", func() {
		It("prints session GUCs", func() {
			gucs := backup.SessionGUCs{ClientEncoding: "UTF8"}

			backup.PrintSessionGUCs(backupfile, tocfile, gucs)
			testhelper.ExpectRegexp(buffer, `SET client_encoding = 'UTF8';
`)
		})
	})
	Describe("PrintCreateDatabaseStatement", func() {
		It("prints a basic CREATE DATABASE statement", func() {
			db := backup.Database{Oid: 1, Name: "testdb", Tablespace: "pg_default"}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateDatabaseStatement(backupfile, tocfile, emptyDB, db, emptyMetadataMap)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "testdb", toc.OBJ_DATABASE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE DATABASE testdb TEMPLATE template0;`)
		})
		It("prints a CREATE DATABASE statement for a reserved keyword named database", func() {
			db := backup.Database{Oid: 1, Name: `"table"`, Tablespace: "pg_default"}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateDatabaseStatement(backupfile, tocfile, emptyDB, db, emptyMetadataMap)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", `"table"`, toc.OBJ_DATABASE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE DATABASE "table" TEMPLATE template0;`)
		})
		It("prints a CREATE DATABASE statement with privileges, an owner, security label, and a comment", func() {
			db := backup.Database{Oid: 1, Name: "testdb", Tablespace: "pg_default"}
			dbMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_DATABASE, true, true, true, true)
			dbMetadata := dbMetadataMap[db.GetUniqueID()]
			dbMetadata.Privileges[0].Create = false
			dbMetadataMap[db.GetUniqueID()] = dbMetadata
			backup.PrintCreateDatabaseStatement(backupfile, tocfile, emptyDB, db, dbMetadataMap)
			expectedStatements := []string{
				`CREATE DATABASE testdb TEMPLATE template0;`,
				`COMMENT ON DATABASE testdb IS 'This is a database comment.';`,
				`ALTER DATABASE testdb OWNER TO testrole;`,
				`REVOKE ALL ON DATABASE testdb FROM PUBLIC;
REVOKE ALL ON DATABASE testdb FROM testrole;
GRANT TEMPORARY,CONNECT ON DATABASE testdb TO testrole;`,
				`SECURITY LABEL FOR dummy ON DATABASE testdb IS 'unclassified';`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)
		})
		It("prints a CREATE DATABASE statement with all modifiers", func() {
			db := backup.Database{Oid: 1, Name: "testdb", Tablespace: "test_tablespace", Encoding: "UTF8", Collate: "en_US.utf-8", CType: "en_US.utf-8"}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateDatabaseStatement(backupfile, tocfile, emptyDB, db, emptyMetadataMap)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE DATABASE testdb TEMPLATE template0 TABLESPACE test_tablespace ENCODING 'UTF8' LC_COLLATE 'en_US.utf-8' LC_CTYPE 'en_US.utf-8';`)
		})
		It("does not print encoding information if it is the same as defaults", func() {
			defaultDB := backup.Database{Oid: 0, Name: "", Tablespace: "", Encoding: "UTF8", Collate: "en_US.utf-8", CType: "en_US.utf-8"}
			db := backup.Database{Oid: 1, Name: "testdb", Tablespace: "test_tablespace", Encoding: "UTF8", Collate: "en_US.utf-8", CType: "en_US.utf-8"}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateDatabaseStatement(backupfile, tocfile, defaultDB, db, emptyMetadataMap)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE DATABASE testdb TEMPLATE template0 TABLESPACE test_tablespace;`)
		})
	})
	Describe("PrintDatabaseGUCs", func() {
		dbname := "testdb"
		defaultOidGUC := "SET default_with_oids TO 'true'"
		searchPathGUC := "SET search_path TO pg_catalog, public"
		defaultStorageGUC := "SET gp_default_storage_options TO 'appendonly=true,blocksize=32768'"

		It("prints single database GUC", func() {
			gucs := []string{defaultOidGUC}

			backup.PrintDatabaseGUCs(backupfile, tocfile, gucs, dbname)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "testdb", toc.OBJ_DATABASE_GUC)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `ALTER DATABASE testdb SET default_with_oids TO 'true';`)
		})
		It("prints multiple database GUCs", func() {
			gucs := []string{defaultOidGUC, searchPathGUC, defaultStorageGUC}

			backup.PrintDatabaseGUCs(backupfile, tocfile, gucs, dbname)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`ALTER DATABASE testdb SET default_with_oids TO 'true';`,
				`ALTER DATABASE testdb SET search_path TO pg_catalog, public;`,
				`ALTER DATABASE testdb SET gp_default_storage_options TO 'appendonly=true,blocksize=32768';`)
		})
	})
	Describe("PrintCreateResourceQueueStatements", func() {
		var emptyResQueueMetadata = backup.MetadataMap{}
		It("prints resource queues", func() {
			someQueue := backup.ResourceQueue{Oid: 1, Name: "some_queue", ActiveStatements: 1, MaxCost: "-1.00", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
			maxCostQueue := backup.ResourceQueue{Oid: 1, Name: `"someMaxCostQueue"`, ActiveStatements: -1, MaxCost: "99.9", CostOvercommit: true, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
			resQueues := []backup.ResourceQueue{someQueue, maxCostQueue}

			backup.PrintCreateResourceQueueStatements(backupfile, tocfile, resQueues, emptyResQueueMetadata)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "some_queue", toc.OBJ_RESOURCE_QUEUE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`CREATE RESOURCE QUEUE some_queue WITH (ACTIVE_STATEMENTS=1);`,
				`CREATE RESOURCE QUEUE "someMaxCostQueue" WITH (MAX_COST=99.9, COST_OVERCOMMIT=TRUE);`)
		})
		It("prints a resource queue with active statements and max cost", func() {
			someActiveMaxCostQueue := backup.ResourceQueue{Oid: 1, Name: `"someActiveMaxCostQueue"`, ActiveStatements: 5, MaxCost: "62.03", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
			resQueues := []backup.ResourceQueue{someActiveMaxCostQueue}

			backup.PrintCreateResourceQueueStatements(backupfile, tocfile, resQueues, emptyResQueueMetadata)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE RESOURCE QUEUE "someActiveMaxCostQueue" WITH (ACTIVE_STATEMENTS=5, MAX_COST=62.03);`)
		})
		It("prints a resource queue with all properties", func() {
			everythingQueue := backup.ResourceQueue{Oid: 1, Name: `"everythingQueue"`, ActiveStatements: 7, MaxCost: "32.80", CostOvercommit: true, MinCost: "1.34", Priority: "low", MemoryLimit: "2GB"}
			resQueues := []backup.ResourceQueue{everythingQueue}

			backup.PrintCreateResourceQueueStatements(backupfile, tocfile, resQueues, emptyResQueueMetadata)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE RESOURCE QUEUE "everythingQueue" WITH (ACTIVE_STATEMENTS=7, MAX_COST=32.80, COST_OVERCOMMIT=TRUE, MIN_COST=1.34, PRIORITY=LOW, MEMORY_LIMIT='2GB');`)
		})
		It("prints a resource queue with a comment", func() {
			commentQueue := backup.ResourceQueue{Oid: 1, Name: `"commentQueue"`, ActiveStatements: 1, MaxCost: "-1.00", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
			resQueues := []backup.ResourceQueue{commentQueue}
			resQueueMetadata := testutils.DefaultMetadataMap(toc.OBJ_RESOURCE_QUEUE, false, false, true, false)

			backup.PrintCreateResourceQueueStatements(backupfile, tocfile, resQueues, resQueueMetadata)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE RESOURCE QUEUE "commentQueue" WITH (ACTIVE_STATEMENTS=1);`,
				`COMMENT ON RESOURCE QUEUE "commentQueue" IS 'This is a resource queue comment.';`)
		})
		It("prints ALTER statement for pg_default resource queue", func() {
			pgDefault := backup.ResourceQueue{Oid: 1, Name: "pg_default", ActiveStatements: 1, MaxCost: "-1.00", CostOvercommit: false, MinCost: "0.00", Priority: "medium", MemoryLimit: "-1"}
			resQueues := []backup.ResourceQueue{pgDefault}

			backup.PrintCreateResourceQueueStatements(backupfile, tocfile, resQueues, emptyResQueueMetadata)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `ALTER RESOURCE QUEUE pg_default WITH (ACTIVE_STATEMENTS=1);`)
		})
	})
	Describe("PrintCreateResourceGroupStatements", func() {
		var emptyResGroupMetadata = backup.MetadataMap{}
		It("prints resource groups", func() {
			testhelper.SetDBVersion(connectionPool, "5.9.0")
			someGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: "15"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30"}
			someGroup2 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 2, Name: "some_group2", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "10"}
			resGroups := []backup.ResourceGroupBefore7{someGroup, someGroup2}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "some_group", toc.OBJ_RESOURCE_GROUP)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`CREATE RESOURCE GROUP some_group WITH (CPU_RATE_LIMIT=10, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=20, MEMORY_SHARED_QUOTA=25, MEMORY_SPILL_RATIO=30, CONCURRENCY=15);`,
				`CREATE RESOURCE GROUP some_group2 WITH (CPU_RATE_LIMIT=20, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=10, CONCURRENCY=25);`)
		})
		It("prints ALTER statement for default_group resource group", func() {
			defaultGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "default_group", Concurrency: "15"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30"}
			resGroups := []backup.ResourceGroupBefore7{defaultGroup}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "default_group", toc.OBJ_RESOURCE_GROUP)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`ALTER RESOURCE GROUP default_group SET MEMORY_LIMIT 20;`,
				`ALTER RESOURCE GROUP default_group SET MEMORY_SHARED_QUOTA 25;`,
				`ALTER RESOURCE GROUP default_group SET MEMORY_SPILL_RATIO 30;`,
				`ALTER RESOURCE GROUP default_group SET CONCURRENCY 15;`,
				`ALTER RESOURCE GROUP default_group SET CPU_RATE_LIMIT 10;`)
		})
		It("prints memory_auditor resource groups", func() {
			testhelper.SetDBVersion(connectionPool, "5.8.0")
			someGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: "15"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30"}
			someGroup2 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 2, Name: "some_group2", Concurrency: "0"}, CPURateLimit: "10", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "10", MemoryAuditor: "1"}
			someGroup3 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 3, Name: "some_group3", Concurrency: "25"}, CPURateLimit: "10", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "10", MemoryAuditor: "0"}
			resGroups := []backup.ResourceGroupBefore7{someGroup, someGroup2, someGroup3}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "some_group", toc.OBJ_RESOURCE_GROUP)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`CREATE RESOURCE GROUP some_group WITH (CPU_RATE_LIMIT=10, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=20, MEMORY_SHARED_QUOTA=25, MEMORY_SPILL_RATIO=30, CONCURRENCY=15);`,
				`CREATE RESOURCE GROUP some_group2 WITH (CPU_RATE_LIMIT=10, MEMORY_AUDITOR=cgroup, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=10, CONCURRENCY=0);`,
				`CREATE RESOURCE GROUP some_group3 WITH (CPU_RATE_LIMIT=10, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=10, CONCURRENCY=25);`)
		})
		It("prints cpuset resource groups", func() {
			testhelper.SetDBVersion(connectionPool, "5.9.0")
			someGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "some_group", Concurrency: "15"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30"}
			someGroup2 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 2, Name: "some_group2", Concurrency: "25", Cpuset: "0-3"}, CPURateLimit: "-1", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "10"}
			resGroups := []backup.ResourceGroupBefore7{someGroup, someGroup2}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "some_group", toc.OBJ_RESOURCE_GROUP)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`CREATE RESOURCE GROUP some_group WITH (CPU_RATE_LIMIT=10, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=20, MEMORY_SHARED_QUOTA=25, MEMORY_SPILL_RATIO=30, CONCURRENCY=15);`,
				`CREATE RESOURCE GROUP some_group2 WITH (CPUSET='0-3', MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=10, CONCURRENCY=25);`)
		})
		// Test for GPDB5x, 'retired' in 6X+ #temp5xResGroup
		It("prints memory_spill_ratio resource groups in new syntax", func() {
			testhelper.SetDBVersion(connectionPool, "5.2.0")

			defaultGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 1, Name: "default_group", Concurrency: "15"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30 MB"}
			adminGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 2, Name: "admin_group", Concurrency: "15"}, CPURateLimit: "10", MemoryLimit: "20", MemorySharedQuota: "25", MemorySpillRatio: "30"}
			someGroup := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 3, Name: "some_group", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40 MB"}
			someGroup2 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 4, Name: "some_group2", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40"}
			resGroups := []backup.ResourceGroupBefore7{defaultGroup, adminGroup, someGroup, someGroup2}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "default_group", toc.OBJ_RESOURCE_GROUP)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`ALTER RESOURCE GROUP default_group SET MEMORY_LIMIT 20;`,
				`ALTER RESOURCE GROUP default_group SET MEMORY_SHARED_QUOTA 25;`,
				`ALTER RESOURCE GROUP default_group SET MEMORY_SPILL_RATIO '30 MB';`,
				`ALTER RESOURCE GROUP default_group SET CONCURRENCY 15;`,
				`ALTER RESOURCE GROUP default_group SET CPU_RATE_LIMIT 10;`,
				`ALTER RESOURCE GROUP admin_group SET MEMORY_LIMIT 20;`,
				`ALTER RESOURCE GROUP admin_group SET MEMORY_SHARED_QUOTA 25;`,
				`ALTER RESOURCE GROUP admin_group SET MEMORY_SPILL_RATIO 30;`,
				`ALTER RESOURCE GROUP admin_group SET CONCURRENCY 15;`,
				`ALTER RESOURCE GROUP admin_group SET CPU_RATE_LIMIT 10;`,
				`CREATE RESOURCE GROUP some_group WITH (CPU_RATE_LIMIT=20, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO='40 MB', CONCURRENCY=25);`,
				`CREATE RESOURCE GROUP some_group2 WITH (CPU_RATE_LIMIT=20, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=40, CONCURRENCY=25);`)
		})
		It("prints correct CREATE RESOURCE GROUP syntax for old resource groups on GPDB 5.8", func() {
			// Memory Auditor reslimittype was added in GPDB 5.8. Make sure the older resource group object will have the proper default.
			testhelper.SetDBVersion(connectionPool, "5.8.0")
			resGroup52 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 3, Name: "resGroup52", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40"}
			resGroup58 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 4, Name: "resGroup58", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40", MemoryAuditor: "1"}
			resGroups := []backup.ResourceGroupBefore7{resGroup52, resGroup58}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`CREATE RESOURCE GROUP resGroup52 WITH (CPU_RATE_LIMIT=20, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=40, CONCURRENCY=25);`,
				`CREATE RESOURCE GROUP resGroup58 WITH (CPU_RATE_LIMIT=20, MEMORY_AUDITOR=cgroup, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=40, CONCURRENCY=25);`)
		})
		It("prints correct CREATE RESOURCE GROUP syntax for old resource groups on GPDB 5.9", func() {
			// Cpuset reslimittype was added in GPDB 5.9. Make sure the older resource group objects
			// will have the proper default. In this case, you either have cpu_rate_limit or cpuset.
			testhelper.SetDBVersion(connectionPool, "5.9.0")
			resGroup52 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 3, Name: "resGroup52", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40"}
			resGroup58 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 4, Name: "resGroup58", Concurrency: "25"}, CPURateLimit: "20", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40", MemoryAuditor: "1"}
			resGroup59 := backup.ResourceGroupBefore7{ResourceGroup: backup.ResourceGroup{Oid: 5, Name: "resGroup59", Concurrency: "25", Cpuset: "1"}, CPURateLimit: "-1", MemoryLimit: "30", MemorySharedQuota: "35", MemorySpillRatio: "40", MemoryAuditor: "1"}
			resGroups := []backup.ResourceGroupBefore7{resGroup52, resGroup58, resGroup59}

			backup.PrintCreateResourceGroupStatementsBefore7(backupfile, tocfile, resGroups, emptyResGroupMetadata)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`CREATE RESOURCE GROUP resGroup52 WITH (CPU_RATE_LIMIT=20, MEMORY_AUDITOR=vmtracker, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=40, CONCURRENCY=25);`,
				`CREATE RESOURCE GROUP resGroup58 WITH (CPU_RATE_LIMIT=20, MEMORY_AUDITOR=cgroup, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=40, CONCURRENCY=25);`,
				`CREATE RESOURCE GROUP resGroup59 WITH (CPUSET='1', MEMORY_AUDITOR=cgroup, MEMORY_LIMIT=30, MEMORY_SHARED_QUOTA=35, MEMORY_SPILL_RATIO=40, CONCURRENCY=25);`)
		})
	})
	Describe("PrintResetResourceGroupStatements", func() {
		It("prints prepare resource groups", func() {
			backup.PrintResetResourceGroupStatements(backupfile, tocfile)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "admin_group", toc.OBJ_RESOURCE_GROUP)
			if connectionPool.Version.IsGPDB() && connectionPool.Version.Before("7") {
				testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
					`ALTER RESOURCE GROUP admin_group SET CPU_RATE_LIMIT 1;`,
					`ALTER RESOURCE GROUP admin_group SET MEMORY_LIMIT 1;`,
					`ALTER RESOURCE GROUP default_group SET CPU_RATE_LIMIT 1;`,
					`ALTER RESOURCE GROUP default_group SET MEMORY_LIMIT 1;`)
			} else { // GPDB7+
				testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
					`ALTER RESOURCE GROUP admin_group SET CPU_MAX_PERCENT 1;`,
					`ALTER RESOURCE GROUP admin_group SET CPU_WEIGHT 100;`,
					`ALTER RESOURCE GROUP default_group SET CPU_MAX_PERCENT 1;`,
					`ALTER RESOURCE GROUP default_group SET CPU_WEIGHT 100;`,
					`ALTER RESOURCE GROUP system_group SET CPU_MAX_PERCENT 1;`,
					`ALTER RESOURCE GROUP system_group SET CPU_WEIGHT 100;`)

			}
		})
	})
	Describe("PrintCreateRoleStatements", func() {
		testrole1 := backup.Role{
			Oid:             1,
			Name:            "testrole1",
			Super:           false,
			Inherit:         false,
			CreateRole:      false,
			CreateDB:        false,
			CanLogin:        false,
			Replication:     false,
			ConnectionLimit: -1,
			Password:        "",
			ValidUntil:      "",
			ResQueue:        "pg_default",
			ResGroup:        "default_group",
			Createrexthttp:  false,
			Createrextgpfd:  false,
			Createwextgpfd:  false,
			Createrexthdfs:  false,
			Createwexthdfs:  false,
			TimeConstraints: []backup.TimeConstraint{},
		}

		testrole2 := backup.Role{
			Oid:             1,
			Name:            `"testRole2"`,
			Super:           true,
			Inherit:         true,
			CreateRole:      true,
			CreateDB:        true,
			CanLogin:        true,
			Replication:     true,
			ConnectionLimit: 4,
			Password:        "md5a8b2c77dfeba4705f29c094592eb3369",
			ValidUntil:      "2099-01-01 00:00:00-08",
			ResQueue:        `"testQueue"`,
			ResGroup:        `"testGroup"`,
			Createrexthttp:  true,
			Createrextgpfd:  true,
			Createwextgpfd:  true,
			Createrexthdfs:  true,
			Createwexthdfs:  true,
			TimeConstraints: []backup.TimeConstraint{
				{
					StartDay:  0,
					StartTime: "13:30:00",
					EndDay:    3,
					EndTime:   "14:30:00",
				}, {
					StartDay:  5,
					StartTime: "00:00:00",
					EndDay:    5,
					EndTime:   "24:00:00",
				},
			},
		}

		getResourceGroupReplace := func() (string, string) {

			resourceGroupReplace1 := ` RESOURCE GROUP default_group`
			resourceGroupReplace2 := `RESOURCE GROUP "testGroup" `
			return resourceGroupReplace1, resourceGroupReplace2
		}

		It("prints basic role", func() {
			roleMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_ROLE, false, false, true, false)
			backup.PrintCreateRoleStatements(backupfile, tocfile, []backup.Role{testrole1}, roleMetadataMap)

			resourceGroupReplace, _ := getResourceGroupReplace()
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "testrole1", toc.OBJ_ROLE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, fmt.Sprintf(`CREATE ROLE testrole1;
ALTER ROLE testrole1 WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB NOLOGIN RESOURCE QUEUE pg_default%s;`, resourceGroupReplace),
				`COMMENT ON ROLE testrole1 IS 'This is a role comment.';`)
		})
		It("prints roles with non-defaults and security label", func() {
			roleMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_ROLE, false, false, true, true)
			backup.PrintCreateRoleStatements(backupfile, tocfile, []backup.Role{testrole2}, roleMetadataMap)

			_, resourceGroupReplace := getResourceGroupReplace()
			expectedStatements := []string{
				fmt.Sprintf(`CREATE ROLE "testRole2";
ALTER ROLE "testRole2" WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION CONNECTION LIMIT 4 PASSWORD 'md5a8b2c77dfeba4705f29c094592eb3369' VALID UNTIL '2099-01-01 00:00:00-08' RESOURCE QUEUE "testQueue" %sCREATEEXTTABLE (protocol='http') CREATEEXTTABLE (protocol='gpfdist', type='readable') CREATEEXTTABLE (protocol='gpfdist', type='writable') CREATEEXTTABLE (protocol='gphdfs', type='readable') CREATEEXTTABLE (protocol='gphdfs', type='writable');`, resourceGroupReplace),
				`ALTER ROLE "testRole2" DENY BETWEEN DAY 0 TIME '13:30:00' AND DAY 3 TIME '14:30:00';`,
				`ALTER ROLE "testRole2" DENY BETWEEN DAY 5 TIME '00:00:00' AND DAY 5 TIME '24:00:00';`,
				`COMMENT ON ROLE "testRole2" IS 'This is a role comment.';`,
				`SECURITY LABEL FOR dummy ON ROLE "testRole2" IS 'unclassified';`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)

		})
		It("prints multiple roles", func() {
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateRoleStatements(backupfile, tocfile, []backup.Role{testrole1, testrole2}, emptyMetadataMap)

			resourceGroupReplace1, resourceGroupReplace2 := getResourceGroupReplace()
			expectedStatements := []string{
				fmt.Sprintf(`CREATE ROLE testrole1;
ALTER ROLE testrole1 WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB NOLOGIN RESOURCE QUEUE pg_default%s;`, resourceGroupReplace1),
				fmt.Sprintf(`CREATE ROLE "testRole2";
ALTER ROLE "testRole2" WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION CONNECTION LIMIT 4 PASSWORD 'md5a8b2c77dfeba4705f29c094592eb3369' VALID UNTIL '2099-01-01 00:00:00-08' RESOURCE QUEUE "testQueue" %sCREATEEXTTABLE (protocol='http') CREATEEXTTABLE (protocol='gpfdist', type='readable') CREATEEXTTABLE (protocol='gpfdist', type='writable') CREATEEXTTABLE (protocol='gphdfs', type='readable') CREATEEXTTABLE (protocol='gphdfs', type='writable');`, resourceGroupReplace2),
				`ALTER ROLE "testRole2" DENY BETWEEN DAY 0 TIME '13:30:00' AND DAY 3 TIME '14:30:00';`,
				`ALTER ROLE "testRole2" DENY BETWEEN DAY 5 TIME '00:00:00' AND DAY 5 TIME '24:00:00';`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)
		})
	})
	Describe("PrintRoleMembershipStatements", func() {
		roleWith := backup.RoleMember{Role: "group", Member: "rolewith", Grantor: "grantor", IsAdmin: true}
		roleWithout := backup.RoleMember{Role: "group", Member: "rolewithout", Grantor: "grantor", IsAdmin: false}
		roleWithoutGrantor := backup.RoleMember{Role: "group", Member: "rolewithoutgrantor", Grantor: "", IsAdmin: false}
		It("prints a role without ADMIN OPTION", func() {
			backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{roleWithout})
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "rolewithout", toc.OBJ_ROLE_GRANT)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `GRANT group TO rolewithout GRANTED BY grantor;`)
		})
		It("prints a role WITH ADMIN OPTION", func() {
			backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{roleWith})
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `GRANT group TO rolewith WITH ADMIN OPTION GRANTED BY grantor;`)
		})
		It("prints multiple roles", func() {
			backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{roleWith, roleWithout})
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer,
				`GRANT group TO rolewith WITH ADMIN OPTION GRANTED BY grantor;`,
				`GRANT group TO rolewithout GRANTED BY grantor;`)
		})
		It("prints a role without a grantor", func() {
			backup.PrintRoleMembershipStatements(backupfile, tocfile, []backup.RoleMember{roleWithoutGrantor})
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `GRANT group TO rolewithoutgrantor;`)
		})
	})
	Describe("PrintRoleGUCStatements", func() {
		It("Prints guc statements for a role", func() {
			roleConfigMap := map[string][]backup.RoleGUC{
				"testrole1": {
					{RoleName: "testrole1", Config: "SET search_path TO public"},
					{RoleName: "testrole1", DbName: "testdb", Config: "SET client_min_messages TO 'error'"},
					{RoleName: "testrole1", Config: "SET gp_default_storage_options TO 'appendonly=true, compresslevel=6, orientation=row, compresstype=none'"}},
			}
			backup.PrintRoleGUCStatements(backupfile, tocfile, roleConfigMap)

			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "testrole1", toc.OBJ_ROLE_GUC)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `ALTER ROLE testrole1 SET search_path TO public;`,
				`ALTER ROLE testrole1 IN DATABASE testdb SET client_min_messages TO 'error';`,
				`ALTER ROLE testrole1 SET gp_default_storage_options TO 'appendonly=true, compresslevel=6, orientation=row, compresstype=none';`)
		})
	})
	Describe("PrintCreateTablespaceStatements", func() {
		expectedTablespace := backup.Tablespace{Oid: 1, Tablespace: "test_tablespace", FileLocation: "test_filespace"}
		It("prints a basic tablespace with a filespace", func() {
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "test_tablespace", toc.OBJ_TABLESPACE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE TABLESPACE test_tablespace FILESPACE test_filespace;`)
		})
		It("prints a tablespace with privileges, an owner, security label, and a comment", func() {
			tablespaceMetadataMap := testutils.DefaultMetadataMap(toc.OBJ_TABLESPACE, true, true, true, true)
			backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, tablespaceMetadataMap)
			expectedStatements := []string{
				`CREATE TABLESPACE test_tablespace FILESPACE test_filespace;`,
				`COMMENT ON TABLESPACE test_tablespace IS 'This is a tablespace comment.';`,
				`ALTER TABLESPACE test_tablespace OWNER TO testrole;`,
				`REVOKE ALL ON TABLESPACE test_tablespace FROM PUBLIC;
REVOKE ALL ON TABLESPACE test_tablespace FROM testrole;
GRANT ALL ON TABLESPACE test_tablespace TO testrole;`,
				`SECURITY LABEL FOR dummy ON TABLESPACE test_tablespace IS 'unclassified';`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)

		})
		It("prints a tablespace with no per-segment tablespaces", func() {
			expectedTablespace := backup.Tablespace{
				Oid: 1, Tablespace: "test_tablespace", FileLocation: "'/data/dir'",
				SegmentLocations: []string{},
			}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "test_tablespace", toc.OBJ_TABLESPACE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE TABLESPACE test_tablespace LOCATION '/data/dir';`)
		})
		It("prints a tablespace with per-segment tablespaces", func() {
			expectedTablespace := backup.Tablespace{
				Oid: 1, Tablespace: "test_tablespace", FileLocation: "'/data/dir'",
				SegmentLocations: []string{"content1='/data/dir1'", "content2='/data/dir2'", "content3='/data/dir3'"},
			}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "test_tablespace", toc.OBJ_TABLESPACE)
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE TABLESPACE test_tablespace LOCATION '/data/dir'
	WITH (content1='/data/dir1', content2='/data/dir2', content3='/data/dir3');`)
		})
		It("prints a tablespace with options", func() {
			expectedTablespace := backup.Tablespace{
				Oid: 1, Tablespace: "test_tablespace", FileLocation: "'/data/dir'",
				SegmentLocations: []string{},
				Options:          "param1=val1, param2=val2",
			}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap)
			testutils.ExpectEntry(tocfile.GlobalEntries, 0, "", "", "test_tablespace", toc.OBJ_TABLESPACE)
			testutils.ExpectEntry(tocfile.GlobalEntries, 1, "", "", "test_tablespace", toc.OBJ_TABLESPACE)
			expectedStatements := []string{`CREATE TABLESPACE test_tablespace LOCATION '/data/dir';`,
				`ALTER TABLESPACE test_tablespace SET (param1 = val1, param2 = val2);`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)
		})
		It("prints a remote tablespace with server, path and handler options", func() {
			expectedTablespace := backup.Tablespace{
				Oid:               1,
				Tablespace:        "remote_ts",
				FileLocation:      "", // Ignored when path is present
				Options:           "server=s3_server, path='/bucket/path', random_page_cost=4",
				Spcfilehandlerbin: "$libdir/dfs_tablespace",
				Spcfilehandlersrc: "remote_file_handler",
			}
			emptyMetadataMap := backup.MetadataMap{}
			backup.PrintCreateTablespaceStatements(backupfile, tocfile, []backup.Tablespace{expectedTablespace}, emptyMetadataMap)

			expectedStatements := []string{
				`CREATE TABLESPACE remote_ts LOCATION '/bucket/path' SERVER s3_server HANDLER '$libdir/dfs_tablespace, remote_file_handler';`,
				`ALTER TABLESPACE remote_ts SET (random_page_cost = 4);`,
			}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)
		})
	})

	Describe("PrintCreateStorageServerStatements", func() {
		It("prints a storage server without options", func() {
			expectedServer := backup.StorageServer{Oid: 1, Server: "test_server"}
			emptyMetadataMap := backup.MetadataMap{}

			backup.PrintCreateStorageServerStatements(backupfile, tocfile, []backup.StorageServer{expectedServer}, emptyMetadataMap)

			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE STORAGE SERVER test_server;`)
		})

		It("prints a storage server with options", func() {
			expectedServer := backup.StorageServer{
				Oid:           1,
				Server:        "test_server",
				ServerOptions: "protocol=s3, region=us-east-1, endpoint=s3.example.com",
			}
			emptyMetadataMap := backup.MetadataMap{}

			backup.PrintCreateStorageServerStatements(backupfile, tocfile, []backup.StorageServer{expectedServer}, emptyMetadataMap)

			// Note: The order is deterministic because we sort the keys.
			expectedStatements := []string{`CREATE STORAGE SERVER test_server OPTIONS(endpoint 's3.example.com', protocol 's3', region 'us-east-1');`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)
		})
	})

	Describe("PrintCreateStorageUserMappingStatements", func() {
		It("prints a storage user mapping without options", func() {
			expectedUserMapping := backup.StorageUserMapping{Oid: 1, User: "testrole", Server: "test_server"}
			backup.PrintCreateStorageUserMappingStatements(backupfile, tocfile, []backup.StorageUserMapping{expectedUserMapping})

			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, `CREATE STORAGE USER MAPPING FOR testrole STORAGE SERVER test_server;`)
		})

		It("prints a storage user mapping with options", func() {
			expectedUserMapping := backup.StorageUserMapping{
				Oid:     1,
				User:    "testrole",
				Server:  "test_server",
				Options: "secretkey=mysecret, accesskey=mykey",
			}

			backup.PrintCreateStorageUserMappingStatements(backupfile, tocfile, []backup.StorageUserMapping{expectedUserMapping})

			// Note: The order is deterministic because we sort the keys.
			expectedStatements := []string{`CREATE STORAGE USER MAPPING FOR testrole STORAGE SERVER test_server OPTIONS (accesskey 'mykey', secretkey 'mysecret');`}
			testutils.AssertBufferContents(tocfile.GlobalEntries, buffer, expectedStatements...)
		})
	})
})
