github.com/tuhaihe/gpbackup@v1.0.3/options/options_test.go (about) 1 package options_test 2 3 import ( 4 "io/ioutil" 5 "os" 6 7 "github.com/DATA-DOG/go-sqlmock" 8 "github.com/tuhaihe/gp-common-go-libs/dbconn" 9 "github.com/tuhaihe/gp-common-go-libs/testhelper" 10 "github.com/tuhaihe/gpbackup/options" 11 "github.com/spf13/pflag" 12 13 . "github.com/onsi/ginkgo/v2" 14 . "github.com/onsi/gomega" 15 ) 16 17 var _ = Describe("options", func() { 18 var ( 19 myflags *pflag.FlagSet 20 ) 21 BeforeEach(func() { 22 myflags = &pflag.FlagSet{} 23 options.SetBackupFlagDefaults(myflags) 24 }) 25 Describe("Options initialization", func() { 26 It("returns no included tables when none specified", func() { 27 subject, err := options.NewOptions(myflags) 28 Expect(err).To(Not(HaveOccurred())) 29 30 includedTables := subject.GetIncludedTables() 31 Expect(includedTables).To(BeEmpty()) 32 originalIncludedTables := subject.GetOriginalIncludedTables() 33 Expect(originalIncludedTables).To(BeEmpty()) 34 }) 35 It("returns the include tables when one table in flag", func() { 36 err := myflags.Set(options.INCLUDE_RELATION, "foo.bar") 37 Expect(err).ToNot(HaveOccurred()) 38 39 subject, err := options.NewOptions(myflags) 40 Expect(err).To(Not(HaveOccurred())) 41 42 includedTables := subject.GetIncludedTables() 43 Expect(includedTables).To(HaveLen(1)) 44 Expect(includedTables[0]).To(Equal("foo.bar")) 45 originalIncludedTables := subject.GetOriginalIncludedTables() 46 Expect(originalIncludedTables[0]).To(Equal("foo.bar")) 47 }) 48 It("returns an include with special characters besides quote and dot", func() { 49 err := myflags.Set(options.INCLUDE_RELATION, `foo '~#$%^&*()_-+[]{}><\|;:/?!\t\n,.bar`) 50 Expect(err).ToNot(HaveOccurred()) 51 subject, err := options.NewOptions(myflags) 52 Expect(err).To(Not(HaveOccurred())) 53 54 includedTables := subject.GetIncludedTables() 55 Expect(includedTables).To(HaveLen(1)) 56 Expect(includedTables[0]).To(Equal(`foo '~#$%^&*()_-+[]{}><\|;:/?!\t\n,.bar`)) 57 }) 58 It("returns all included tables when multiple individual flags provided", func() { 59 err := myflags.Set(options.INCLUDE_RELATION, "foo.bar") 60 Expect(err).ToNot(HaveOccurred()) 61 err = myflags.Set(options.INCLUDE_RELATION, "bar.baz") 62 Expect(err).ToNot(HaveOccurred()) 63 64 subject, err := options.NewOptions(myflags) 65 Expect(err).To(Not(HaveOccurred())) 66 67 includedTables := subject.GetIncludedTables() 68 Expect(includedTables).To(HaveLen(2)) 69 Expect(includedTables[0]).To(Equal("foo.bar")) 70 Expect(includedTables[1]).To(Equal("bar.baz")) 71 }) 72 It("returns the text-file tables when specified", func() { 73 file, err := ioutil.TempFile("/tmp", "gpbackup_test_options*.txt") 74 Expect(err).To(Not(HaveOccurred())) 75 defer func() { 76 _ = os.Remove(file.Name()) 77 }() 78 _, err = file.WriteString("myschema.mytable\n") 79 Expect(err).To(Not(HaveOccurred())) 80 _, err = file.WriteString("myschema.mytable2\n") 81 Expect(err).To(Not(HaveOccurred())) 82 err = file.Close() 83 Expect(err).To(Not(HaveOccurred())) 84 85 err = myflags.Set(options.INCLUDE_RELATION_FILE, file.Name()) 86 Expect(err).ToNot(HaveOccurred()) 87 subject, err := options.NewOptions(myflags) 88 Expect(err).To(Not(HaveOccurred())) 89 90 includedTables := subject.GetIncludedTables() 91 Expect(includedTables).To(HaveLen(2)) 92 Expect(includedTables[0]).To(Equal("myschema.mytable")) 93 Expect(includedTables[1]).To(Equal("myschema.mytable2")) 94 }) 95 It("sets the INCLUDE_RELATIONS flag from file", func() { 96 file, err := ioutil.TempFile("/tmp", "gpbackup_test_options*.txt") 97 Expect(err).To(Not(HaveOccurred())) 98 defer func() { 99 _ = os.Remove(file.Name()) 100 }() 101 _, err = file.WriteString("myschema.mytable\n") 102 Expect(err).To(Not(HaveOccurred())) 103 _, err = file.WriteString("myschema.mytable2\n") 104 Expect(err).To(Not(HaveOccurred())) 105 err = file.Close() 106 Expect(err).To(Not(HaveOccurred())) 107 108 err = myflags.Set(options.INCLUDE_RELATION_FILE, file.Name()) 109 Expect(err).ToNot(HaveOccurred()) 110 _, err = options.NewOptions(myflags) 111 Expect(err).To(Not(HaveOccurred())) 112 113 includedTables, err := myflags.GetStringArray(options.INCLUDE_RELATION) 114 Expect(err).ToNot(HaveOccurred()) 115 Expect(includedTables).To(HaveLen(2)) 116 Expect(includedTables[0]).To(Equal("myschema.mytable")) 117 Expect(includedTables[1]).To(Equal("myschema.mytable2")) 118 }) 119 It("skips empty lines in files provided for filtering tables", func() { 120 file, err := ioutil.TempFile("/tmp", "gpbackup_test_options*.txt") 121 Expect(err).To(Not(HaveOccurred())) 122 defer func() { 123 _ = os.Remove(file.Name()) 124 }() 125 _, err = file.WriteString("myschema.mytable\n") 126 Expect(err).To(Not(HaveOccurred())) 127 _, err = file.WriteString("\n") 128 Expect(err).To(Not(HaveOccurred())) 129 Expect(err).To(Not(HaveOccurred())) 130 _, err = file.WriteString("\n") 131 Expect(err).To(Not(HaveOccurred())) 132 _, err = file.WriteString("myschema.mytable2\n") 133 Expect(err).To(Not(HaveOccurred())) 134 Expect(err).To(Not(HaveOccurred())) 135 _, err = file.WriteString("\n") 136 Expect(err).To(Not(HaveOccurred())) 137 _, err = file.WriteString("\n") 138 err = file.Close() 139 Expect(err).To(Not(HaveOccurred())) 140 141 err = myflags.Set(options.EXCLUDE_RELATION_FILE, file.Name()) 142 Expect(err).ToNot(HaveOccurred()) 143 _, err = options.NewOptions(myflags) 144 Expect(err).To(Not(HaveOccurred())) 145 146 excludedTables, err := myflags.GetStringArray(options.EXCLUDE_RELATION) 147 Expect(err).ToNot(HaveOccurred()) 148 Expect(excludedTables).To(HaveLen(2)) 149 Expect(excludedTables[0]).To(Equal("myschema.mytable")) 150 Expect(excludedTables[1]).To(Equal("myschema.mytable2")) 151 }) 152 It("skips empty lines in files provided for filtering schemas", func() { 153 file, err := ioutil.TempFile("/tmp", "gpbackup_test_options*.txt") 154 Expect(err).To(Not(HaveOccurred())) 155 defer func() { 156 _ = os.Remove(file.Name()) 157 }() 158 _, err = file.WriteString("myschema1\n") 159 Expect(err).To(Not(HaveOccurred())) 160 _, err = file.WriteString("\n") 161 Expect(err).To(Not(HaveOccurred())) 162 Expect(err).To(Not(HaveOccurred())) 163 _, err = file.WriteString("\n") 164 Expect(err).To(Not(HaveOccurred())) 165 _, err = file.WriteString("myschema2\n") 166 Expect(err).To(Not(HaveOccurred())) 167 Expect(err).To(Not(HaveOccurred())) 168 _, err = file.WriteString("\n") 169 Expect(err).To(Not(HaveOccurred())) 170 _, err = file.WriteString("\n") 171 err = file.Close() 172 Expect(err).To(Not(HaveOccurred())) 173 174 err = myflags.Set(options.INCLUDE_SCHEMA_FILE, file.Name()) 175 Expect(err).ToNot(HaveOccurred()) 176 _, err = options.NewOptions(myflags) 177 Expect(err).To(Not(HaveOccurred())) 178 179 includedSchemas, err := myflags.GetStringArray(options.INCLUDE_SCHEMA) 180 Expect(err).ToNot(HaveOccurred()) 181 Expect(includedSchemas).To(HaveLen(2)) 182 Expect(includedSchemas[0]).To(Equal("myschema1")) 183 Expect(includedSchemas[1]).To(Equal("myschema2")) 184 }) 185 It("it remembers flag values for INCLUDE_SCHEMA, EXCLUDE*, LEAF_PARTITION_DATA", func() { 186 err := myflags.Set(options.INCLUDE_SCHEMA, "my include schema") 187 Expect(err).ToNot(HaveOccurred()) 188 err = myflags.Set(options.EXCLUDE_SCHEMA, "my exclude schema") 189 Expect(err).ToNot(HaveOccurred()) 190 err = myflags.Set(options.LEAF_PARTITION_DATA, "true") 191 Expect(err).ToNot(HaveOccurred()) 192 193 subject, err := options.NewOptions(myflags) 194 Expect(err).To(Not(HaveOccurred())) 195 196 Expect(subject.GetIncludedSchemas()[0]).To(Equal("my include schema")) 197 Expect(subject.GetExcludedSchemas()[0]).To(Equal("my exclude schema")) 198 }) 199 It("returns an error upon invalid inclusions", func() { 200 err := myflags.Set(options.INCLUDE_RELATION, "foo") 201 Expect(err).ToNot(HaveOccurred()) 202 _, err = options.NewOptions(myflags) 203 Expect(err).To(HaveOccurred()) 204 }) 205 Describe("AddIncludeRelation", func() { 206 It("it adds a relation", func() { 207 subject, err := options.NewOptions(myflags) 208 Expect(err).To(Not(HaveOccurred())) 209 subject.AddIncludedRelation("public.foobar") 210 Expect(subject.GetIncludedTables()).To(Equal([]string{"public.foobar"})) 211 Expect(subject.GetOriginalIncludedTables()).To(BeEmpty()) 212 }) 213 }) 214 }) 215 Describe("SeparateSchemaAndTable", func() { 216 It("properly splits the strings", func() { 217 tableList := []string{"foo.Bar", "FOO.Bar", "FO!@#.BAR"} 218 expectedFqn := []options.FqnStruct{ 219 {SchemaName: `foo`, TableName: `Bar`}, 220 {SchemaName: `FOO`, TableName: `Bar`}, 221 {SchemaName: `FO!@#`, TableName: `BAR`}, 222 } 223 resultFqn, err := options.SeparateSchemaAndTable(tableList) 224 Expect(err).ToNot(HaveOccurred()) 225 Expect(resultFqn).To(Equal(expectedFqn)) 226 }) 227 It("fails to split TableName", func() { 228 tableList := []string{"foo."} 229 _, err := options.SeparateSchemaAndTable(tableList) 230 Expect(err).To(HaveOccurred()) 231 Expect(err.Error()).To(ContainSubstring("foo.")) 232 }) 233 It("fails to split SchemaName", func() { 234 tableList := []string{".bar"} 235 _, err := options.SeparateSchemaAndTable(tableList) 236 Expect(err).To(HaveOccurred()) 237 Expect(err.Error()).To(ContainSubstring(".bar")) 238 }) 239 It("fails to split SchemaName or tableName (no '.')", func() { 240 tableList := []string{"foobar"} 241 _, err := options.SeparateSchemaAndTable(tableList) 242 Expect(err).To(HaveOccurred()) 243 Expect(err.Error()).To(ContainSubstring("foobar")) 244 }) 245 It("fails when there are more than one dots", func() { 246 // todo in a future story, establish a way for users to escape dots to show us which one is *in* the name versus the dot that divides schemaname from tablename 247 tableList := []string{"foobar.baz.bam"} 248 _, err := options.SeparateSchemaAndTable(tableList) 249 Expect(err).To(HaveOccurred()) 250 Expect(err.Error()).To(ContainSubstring("foobar.baz.bam")) 251 }) 252 }) 253 Describe("QuoteTableNames", func() { 254 var ( 255 conn *dbconn.DBConn 256 mockdb sqlmock.Sqlmock 257 ) 258 BeforeEach(func() { 259 conn, mockdb, _, _, _ = testhelper.SetupTestEnvironment() 260 }) 261 262 It("returns empty result if given empty list", func() { 263 tablenames := make([]string, 0) 264 quotedTableNames, err := options.QuoteTableNames(conn, tablenames) 265 Expect(err).To(Not(HaveOccurred())) 266 Expect(tablenames).To(Equal(quotedTableNames)) 267 }) 268 It("returns a single result when given a single fqn", func() { 269 tablenames := []string{"public.foo"} 270 queryMock := mockdb.ExpectQuery("SELECT quote_ident") 271 resultRows := sqlmock.NewRows([]string{"schemaname", "tablename"}). 272 AddRow("public", "foo") 273 274 queryMock.WillReturnRows(resultRows) 275 276 quotedTableNames, err := options.QuoteTableNames(conn, tablenames) 277 Expect(err).To(Not(HaveOccurred())) 278 Expect(tablenames).To(Equal(quotedTableNames)) 279 }) 280 It("returns an array of correctly formatted fqn's", func() { 281 tablenames := []string{"public.one", "public.two", "public.three"} 282 283 queryMock := mockdb.ExpectQuery("SELECT quote_ident") 284 resultRows := sqlmock.NewRows([]string{"schemaname", "tablename"}). 285 AddRow("public", "one") 286 queryMock.WillReturnRows(resultRows) 287 288 queryMock = mockdb.ExpectQuery("SELECT quote_ident") 289 resultRows = sqlmock.NewRows([]string{"schemaname", "tablename"}). 290 AddRow("public", "two") 291 queryMock.WillReturnRows(resultRows) 292 293 queryMock = mockdb.ExpectQuery("SELECT quote_ident") 294 resultRows = sqlmock.NewRows([]string{"schemaname", "tablename"}). 295 AddRow("public", "three") 296 queryMock.WillReturnRows(resultRows) 297 298 quotedTableNames, err := options.QuoteTableNames(conn, tablenames) 299 Expect(err).To(Not(HaveOccurred())) 300 Expect(tablenames).To(Equal(quotedTableNames)) 301 }) 302 // // todo handle embedded dots 303 // PIt("handles dots within schema or tablename", func() { 304 // }) 305 // // todo handle embedded commas 306 // PIt("handles commas within schema or tablename", func() { 307 // }) 308 // // todo handle embedded quotes 309 // PIt("handles quotes within schema or tablename", func() { 310 // }) 311 // 312 }) 313 })