github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/vent/sqldb/sqldb_integration_test.go (about) 1 // +build integration 2 3 package sqldb_test 4 5 import ( 6 "database/sql" 7 "fmt" 8 "testing" 9 "time" 10 11 "github.com/hyperledger/burrow/vent/config" 12 "github.com/hyperledger/burrow/vent/sqldb" 13 "github.com/hyperledger/burrow/vent/sqldb/adapters" 14 "github.com/hyperledger/burrow/vent/sqlsol" 15 "github.com/hyperledger/burrow/vent/test" 16 "github.com/hyperledger/burrow/vent/types" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func testSynchronizeDB(t *testing.T, cfg *config.VentConfig) { 22 t.Run(fmt.Sprintf("%s: successfully creates database tables and synchronizes db", cfg.DBAdapter), 23 func(t *testing.T) { 24 goodJSON := test.GoodJSONConfFile(t) 25 26 byteValue := []byte(goodJSON) 27 tableStructure, err := sqlsol.NewProjectionFromBytes(byteValue) 28 require.NoError(t, err) 29 30 db, cleanUpDB := test.NewTestDB(t, cfg) 31 defer cleanUpDB() 32 33 err = db.Ping() 34 require.NoError(t, err) 35 36 err = db.SynchronizeDB(test.ChainID, tableStructure.Tables) 37 require.NoError(t, err) 38 }) 39 } 40 41 func testCleanDB(t *testing.T, cfg *config.VentConfig) { 42 t.Run(fmt.Sprintf("%s: successfully creates tables, updates test.ChainID and drops all tables", cfg.DBAdapter), 43 func(t *testing.T) { 44 byteValue := []byte(test.GoodJSONConfFile(t)) 45 tableStructure, err := sqlsol.NewProjectionFromBytes(byteValue) 46 require.NoError(t, err) 47 48 db, cleanUpDB := test.NewTestDB(t, cfg) 49 defer cleanUpDB() 50 51 err = db.Ping() 52 require.NoError(t, err) 53 54 err = db.SynchronizeDB(test.ChainID, tableStructure.Tables) 55 require.NoError(t, err) 56 57 err = db.CleanTables(test.ChainID, test.BurrowVersion) 58 require.NoError(t, err) 59 }) 60 } 61 62 func testRestore(t *testing.T, cfg *config.VentConfig) { 63 t.Run(fmt.Sprintf("%s: can restore from vent logging", cfg.DBAdapter), 64 func(t *testing.T) { 65 db, closeDB := test.NewTestDB(t, cfg) 66 defer closeDB() 67 68 errp := db.Ping() 69 require.NoError(t, errp) 70 71 // new 72 str, dat := getBlock() 73 err := db.SetBlock(test.ChainID, str, dat) 74 require.NoError(t, err) 75 76 // restore to new table 77 prefix := "RESTORED" 78 err = db.RestoreDB(time.Time{}, prefix) 79 require.NoError(t, err) 80 81 for table := range dat.Tables { 82 assertTablesEqual(t, db, table, fmt.Sprintf("%s_%s", prefix, table)) 83 } 84 85 for table := range dat.Tables { 86 dropQuery := db.DBAdapter.DropTableQuery(table) 87 _, err = db.DB.Exec(dropQuery) 88 require.NoError(t, err) 89 } 90 91 // restore in-place over original tables 92 err = db.RestoreDB(time.Time{}, "") 93 require.NoError(t, err) 94 95 for table := range dat.Tables { 96 assertTablesEqual(t, db, table, fmt.Sprintf("%s_%s", prefix, table)) 97 } 98 }) 99 } 100 101 func testSetBlock(t *testing.T, cfg *config.VentConfig) { 102 t.Run(fmt.Sprintf("%s: successfully inserts a block", cfg.DBAdapter), 103 func(t *testing.T) { 104 db, closeDB := test.NewTestDB(t, cfg) 105 defer closeDB() 106 107 err := db.Ping() 108 require.NoError(t, err) 109 110 // new 111 eventTables, eventData := getBlock() 112 err = db.SetBlock(test.ChainID, eventTables, eventData) 113 require.NoError(t, err) 114 115 // read 116 _, err = db.LastBlockHeight(test.ChainID) 117 require.NoError(t, err) 118 119 _, err = db.GetBlock(test.ChainID, eventData.BlockHeight) 120 require.NoError(t, err) 121 122 // alter 123 eventTables, eventData = getAlterBlock() 124 err = db.SetBlock(test.ChainID, eventTables, eventData) 125 require.NoError(t, err) 126 127 //restore 128 err = db.RestoreDB(time.Time{}, "RESTORED") 129 require.NoError(t, err) 130 131 }) 132 133 t.Run(fmt.Sprintf("%s: successfully creates an empty table", cfg.DBAdapter), func(t *testing.T) { 134 db, closeDB := test.NewTestDB(t, cfg) 135 defer closeDB() 136 137 errp := db.Ping() 138 require.NoError(t, errp) 139 140 //table 1 141 tables := map[string]*types.SQLTable{ 142 "AllDataTypesTable": { 143 Name: "AllDataTypesTable", 144 Columns: []*types.SQLTableColumn{ 145 {Name: "test_id", Type: types.SQLColumnTypeSerial, Primary: true}, 146 {Name: "col1", Type: types.SQLColumnTypeBool, Primary: false}, 147 {Name: "col2", Type: types.SQLColumnTypeByteA, Primary: false}, 148 {Name: "col3", Type: types.SQLColumnTypeInt, Primary: false}, 149 {Name: "col4", Type: types.SQLColumnTypeText, Primary: false}, 150 {Name: "col5", Type: types.SQLColumnTypeTimeStamp, Primary: false}, 151 {Name: "col6", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 152 }, 153 }, 154 } 155 156 err := db.SynchronizeDB(test.ChainID, tables) 157 require.NoError(t, err) 158 }) 159 } 160 161 func getBlock() (types.EventTables, types.EventData) { 162 longtext := "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM" 163 longtext = fmt.Sprintf("%s %s %s %s %s", longtext, longtext, longtext, longtext, longtext) 164 165 //table 1 166 table1 := &types.SQLTable{ 167 Name: "test_table1", 168 Columns: []*types.SQLTableColumn{ 169 {Name: "test_id", Type: types.SQLColumnTypeInt, Primary: true}, 170 {Name: "col1", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 171 {Name: "col2", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 172 {Name: "_height", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 173 {Name: "col4", Type: types.SQLColumnTypeText, Primary: false}, 174 {Name: "colV", Type: types.SQLColumnTypeVarchar, Length: 400, Primary: false}, 175 {Name: "colT", Type: types.SQLColumnTypeText, Length: 0, Primary: false}, 176 }, 177 } 178 179 //table 2 180 table2 := &types.SQLTable{ 181 Name: "test_table2", 182 Columns: []*types.SQLTableColumn{ 183 {Name: "_height", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: true}, 184 {Name: "sid_id", Type: types.SQLColumnTypeInt, Primary: true}, 185 {Name: "field_1", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 186 {Name: "field_2", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 187 }, 188 } 189 190 //table 3 191 table3 := &types.SQLTable{ 192 Name: "test_table3", 193 Columns: []*types.SQLTableColumn{ 194 {Name: "_height", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: true}, 195 {Name: "val", Type: types.SQLColumnTypeInt, Primary: false}, 196 }, 197 } 198 199 //table 4 200 table4 := &types.SQLTable{ 201 Name: "test_table4", 202 Columns: []*types.SQLTableColumn{ 203 {Name: "index", Type: types.SQLColumnTypeInt, Primary: true}, 204 {Name: "time", Type: types.SQLColumnTypeTimeStamp, Primary: false}, 205 {Name: "_height", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: false}, 206 }, 207 } 208 209 str := make(types.EventTables) 210 str["1"] = table1 211 str["2"] = table2 212 str["3"] = table3 213 str["4"] = table4 214 215 //---------------------------------------data------------------------------------- 216 var dat types.EventData 217 dat.BlockHeight = 2134234 218 dat.Tables = make(map[string]types.EventDataTable) 219 220 var rows1 []types.EventDataRow 221 rows1 = append(rows1, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"test_id": "1", "col1": "text11", "col2": "text12", "_height": dat.BlockHeight, "col4": "14", "colV": longtext, "colT": longtext}}) 222 rows1 = append(rows1, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"test_id": "2", "col1": "text21", "col2": "text22", "_height": dat.BlockHeight, "col4": "24", "colV": longtext, "colT": longtext}}) 223 rows1 = append(rows1, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"test_id": "3", "col1": "text31", "col2": "text32", "_height": dat.BlockHeight, "col4": "34", "colV": longtext, "colT": longtext}}) 224 rows1 = append(rows1, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"test_id": "4", "col1": "text41", "col3": "text43", "_height": dat.BlockHeight, "colV": longtext, "colT": longtext}}) 225 rows1 = append(rows1, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"test_id": "1", "col1": "upd", "col2": "upd", "_height": dat.BlockHeight, "col4": "upd", "colV": longtext, "colT": longtext}}) 226 dat.Tables["test_table1"] = rows1 227 228 var rows2 []types.EventDataRow 229 rows2 = append(rows2, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "sid_id": "1", "field_1": "A", "field_2": "B"}}) 230 rows2 = append(rows2, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "sid_id": "2", "field_1": "C", "field_2": ""}}) 231 rows2 = append(rows2, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "sid_id": "3", "field_1": "D", "field_2": "E"}}) 232 rows2 = append(rows2, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "sid_id": "1", "field_1": "F"}}) 233 rows2 = append(rows2, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "sid_id": "1", "field_2": "U"}}) 234 dat.Tables["test_table2"] = rows2 235 236 var rows3 []types.EventDataRow 237 rows3 = append(rows3, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": "0123456789ABCDEF1", "val": "1"}}) 238 rows3 = append(rows3, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": "0123456789ABCDEF2", "val": "2"}}) 239 rows3 = append(rows3, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": "0123456789ABCDEFX", "val": "-1"}}) 240 rows3 = append(rows3, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight}}) 241 dat.Tables["test_table3"] = rows3 242 243 var rows4 []types.EventDataRow 244 rows4 = append(rows4, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "time": "2006-01-01 15:04:05", "index": "1"}}) 245 rows4 = append(rows4, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "time": "2006-01-02 15:04:05", "index": "2"}}) 246 rows4 = append(rows4, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "time": "2006-01-03 15:04:05", "index": "3"}}) 247 rows4 = append(rows4, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "time": "2006-01-03 15:04:05", "index": "4"}}) 248 rows4 = append(rows4, types.EventDataRow{Action: types.ActionDelete, RowData: map[string]interface{}{"_height": dat.BlockHeight, "time": "2006-01-03 15:04:05", "index": "3"}}) 249 dat.Tables["test_table4"] = rows4 250 251 return str, dat 252 } 253 254 func getAlterBlock() (types.EventTables, types.EventData) { 255 //table 3 256 table3 := &types.SQLTable{ 257 Name: "test_table3", 258 Columns: []*types.SQLTableColumn{ 259 {Name: "_height", Type: types.SQLColumnTypeVarchar, Length: 100, Primary: true}, 260 {Name: "val", Type: types.SQLColumnTypeInt, Primary: false}, 261 {Name: "val_alter", Type: types.SQLColumnTypeInt, Primary: false}, 262 }, 263 } 264 265 str := make(types.EventTables) 266 str["3"] = table3 267 268 //---------------------------------------data------------------------------------- 269 var dat types.EventData 270 dat.BlockHeight = 23423423 271 dat.Tables = make(map[string]types.EventDataTable) 272 273 var rows5 []types.EventDataRow 274 rows5 = append(rows5, types.EventDataRow{Action: types.ActionUpsert, RowData: map[string]interface{}{"_height": dat.BlockHeight, "val": "1", "val_alter": "1"}}) 275 dat.Tables["test_table3"] = rows5 276 277 return str, dat 278 } 279 280 func assertTablesEqual(t *testing.T, db *sqldb.SQLDB, table1, table2 string) { 281 cols1, rows1 := selectAll(t, db, table1) 282 cols2, rows2 := selectAll(t, db, table2) 283 284 assert.Equal(t, cols1, cols2, "columns should be equal") 285 for i, r1 := range rows1 { 286 r2 := rows2[i] 287 assert.Equal(t, r1, r2, "each row should be equal") 288 } 289 } 290 291 func selectAll(t *testing.T, db *sqldb.SQLDB, tablename string) (columns []string, rows []map[string]interface{}) { 292 // language=SQL 293 selectQuery := adapters.Cleanf("SELECT * FROM %s", db.DBAdapter.SchemaName(tablename)) 294 sqlRows, err := db.DB.Query(selectQuery) 295 require.NoError(t, err) 296 defer sqlRows.Close() 297 298 cols, err := sqlRows.Columns() 299 require.NoError(t, err) 300 for sqlRows.Next() { 301 row := rowMap(t, cols, sqlRows) 302 rows = append(rows, row) 303 } 304 return cols, rows 305 } 306 307 func rowMap(t *testing.T, cols []string, rows *sql.Rows) map[string]interface{} { 308 vals := scanValues(len(cols)) 309 err := rows.Scan(vals...) 310 require.NoError(t, err) 311 mp := make(map[string]interface{}, len(cols)) 312 313 for i, v := range vals { 314 iface := v.(*interface{}) 315 if iface != nil { 316 // truly go at its most beautiful 317 switch iv := (*iface).(type) { 318 case []byte: 319 str := string(iv) 320 mp[cols[i]] = str 321 default: 322 mp[cols[i]] = iv 323 } 324 } 325 } 326 return mp 327 } 328 329 func scanValues(n int) []interface{} { 330 vals := make([]interface{}, n) 331 for i := 0; i < n; i++ { 332 vals[i] = new(interface{}) 333 } 334 return vals 335 }