@@ -951,6 +951,181 @@ func verifyDumpOutput(t *testing.T, output string) {
951951 }
952952}
953953
954+ func TestIgnorePrivileges (t * testing.T ) {
955+ if testing .Short () {
956+ t .Skip ("Skipping integration test in short mode" )
957+ }
958+
959+ embeddedPG := testutil .SetupPostgres (t )
960+ defer embeddedPG .Stop ()
961+ conn , host , port , dbname , user , password := testutil .ConnectToPostgres (t , embeddedPG )
962+ defer conn .Close ()
963+
964+ containerInfo := & struct {
965+ Conn * sql.DB
966+ Host string
967+ Port int
968+ DBName string
969+ User string
970+ Password string
971+ }{
972+ Conn : conn ,
973+ Host : host ,
974+ Port : port ,
975+ DBName : dbname ,
976+ User : user ,
977+ Password : password ,
978+ }
979+
980+ // Create schema with roles and privileges
981+ setupSQL := `
982+ CREATE TABLE users (
983+ id SERIAL PRIMARY KEY,
984+ name TEXT NOT NULL,
985+ email TEXT NOT NULL
986+ );
987+
988+ CREATE TABLE orders (
989+ id SERIAL PRIMARY KEY,
990+ user_id INTEGER REFERENCES users(id),
991+ total_amount DECIMAL(10,2) NOT NULL
992+ );
993+
994+ DO $$
995+ BEGIN
996+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_reader') THEN
997+ CREATE ROLE app_reader;
998+ END IF;
999+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'deploy_bot') THEN
1000+ CREATE ROLE deploy_bot;
1001+ END IF;
1002+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'admin_role') THEN
1003+ CREATE ROLE admin_role;
1004+ END IF;
1005+ END $$;
1006+
1007+ -- Privileges to keep
1008+ GRANT SELECT ON users TO app_reader;
1009+ GRANT SELECT ON orders TO app_reader;
1010+
1011+ -- Privileges to ignore (deploy_bot)
1012+ GRANT ALL ON users TO deploy_bot;
1013+ GRANT ALL ON orders TO deploy_bot;
1014+
1015+ -- Privileges to ignore (admin_role)
1016+ GRANT SELECT, INSERT, UPDATE, DELETE ON users TO admin_role;
1017+
1018+ -- Default privileges to keep
1019+ ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO app_reader;
1020+
1021+ -- Default privileges to ignore (deploy_bot)
1022+ ALTER DEFAULT PRIVILEGES GRANT ALL ON TABLES TO deploy_bot;
1023+ `
1024+ _ , err := conn .Exec (setupSQL )
1025+ if err != nil {
1026+ t .Fatalf ("Failed to create test schema: %v" , err )
1027+ }
1028+
1029+ originalWd , err := os .Getwd ()
1030+ if err != nil {
1031+ t .Fatalf ("Failed to get current working directory: %v" , err )
1032+ }
1033+ defer os .Chdir (originalWd )
1034+
1035+ tmpDir := t .TempDir ()
1036+ os .Chdir (tmpDir )
1037+
1038+ // Create .pgschemaignore with privileges section
1039+ ignoreContent := `[privileges]
1040+ patterns = ["deploy_bot", "admin_*"]
1041+
1042+ [default_privileges]
1043+ patterns = ["deploy_bot"]
1044+ `
1045+ err = os .WriteFile (".pgschemaignore" , []byte (ignoreContent ), 0644 )
1046+ if err != nil {
1047+ t .Fatalf ("Failed to create .pgschemaignore: %v" , err )
1048+ }
1049+
1050+ t .Run ("dump" , func (t * testing.T ) {
1051+ output := executeIgnoreDumpCommand (t , containerInfo )
1052+
1053+ // Privileges for app_reader should be present
1054+ if ! strings .Contains (output , "app_reader" ) {
1055+ t .Error ("Dump should include privileges for app_reader" )
1056+ }
1057+
1058+ // Privileges for deploy_bot should be ignored
1059+ if strings .Contains (output , "deploy_bot" ) {
1060+ t .Error ("Dump should not include privileges for deploy_bot (ignored)" )
1061+ }
1062+
1063+ // Privileges for admin_role should be ignored (matches admin_*)
1064+ if strings .Contains (output , "admin_role" ) {
1065+ t .Error ("Dump should not include privileges for admin_role (ignored by admin_* pattern)" )
1066+ }
1067+ })
1068+
1069+ t .Run ("plan" , func (t * testing.T ) {
1070+ // Create schema file that adds new privileges
1071+ schemaSQL := `
1072+ CREATE TABLE users (
1073+ id SERIAL PRIMARY KEY,
1074+ name TEXT NOT NULL,
1075+ email TEXT NOT NULL
1076+ );
1077+
1078+ CREATE TABLE orders (
1079+ id SERIAL PRIMARY KEY,
1080+ user_id INTEGER REFERENCES users(id),
1081+ total_amount DECIMAL(10,2) NOT NULL
1082+ );
1083+
1084+ DO $$
1085+ BEGIN
1086+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_reader') THEN
1087+ CREATE ROLE app_reader;
1088+ END IF;
1089+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'deploy_bot') THEN
1090+ CREATE ROLE deploy_bot;
1091+ END IF;
1092+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'admin_role') THEN
1093+ CREATE ROLE admin_role;
1094+ END IF;
1095+ END $$;
1096+
1097+ -- Keep these privileges
1098+ GRANT SELECT ON users TO app_reader;
1099+ GRANT SELECT ON orders TO app_reader;
1100+
1101+ -- These should be ignored in plan
1102+ GRANT ALL ON users TO deploy_bot;
1103+ GRANT ALL ON orders TO deploy_bot;
1104+ GRANT SELECT, INSERT, UPDATE, DELETE ON users TO admin_role;
1105+
1106+ -- Default privileges
1107+ ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO app_reader;
1108+ ALTER DEFAULT PRIVILEGES GRANT ALL ON TABLES TO deploy_bot;
1109+ `
1110+ schemaFile := "schema_privs.sql"
1111+ err := os .WriteFile (schemaFile , []byte (schemaSQL ), 0644 )
1112+ if err != nil {
1113+ t .Fatalf ("Failed to create schema file: %v" , err )
1114+ }
1115+ defer os .Remove (schemaFile )
1116+
1117+ output := executeIgnorePlanCommand (t , containerInfo , schemaFile )
1118+
1119+ // Plan should not contain any changes for ignored roles
1120+ if strings .Contains (output , "deploy_bot" ) {
1121+ t .Error ("Plan should not include changes for deploy_bot (ignored)" )
1122+ }
1123+ if strings .Contains (output , "admin_role" ) {
1124+ t .Error ("Plan should not include changes for admin_role (ignored)" )
1125+ }
1126+ })
1127+ }
1128+
9541129// verifyPlanOutput checks that plan output excludes ignored objects
9551130func verifyPlanOutput (t * testing.T , output string ) {
9561131 // Changes that should appear in plan (regular objects)
0 commit comments