github.com/tobgu/qframe@v0.4.0/qframe_sql_test.go (about) 1 package qframe_test 2 3 import ( 4 "database/sql" 5 "database/sql/driver" 6 "io" 7 "testing" 8 9 "github.com/tobgu/qframe" 10 qsql "github.com/tobgu/qframe/config/sql" 11 ) 12 13 // MockDriver implements a fake SQL driver for testing. 14 type MockDriver struct { 15 t *testing.T 16 // expected SQL query 17 query string 18 // results holds values that are 19 // returned from a database query 20 results struct { 21 // column names for each row of values 22 columns []string 23 // each value for each row 24 values [][]driver.Value 25 } 26 // args holds expected values 27 args struct { 28 // values we expect to be given 29 // to the database 30 values [][]driver.Value 31 } 32 33 // optional statement Query implementation 34 mockQuery MockQuery 35 } 36 37 func (m MockDriver) Open(name string) (driver.Conn, error) { 38 stmt := &MockStmt{ 39 t: m.t, 40 values: m.args.values, 41 rows: &MockRows{ 42 t: m.t, 43 columns: m.results.columns, 44 values: m.results.values, 45 }, 46 mockQuery: m.mockQuery, 47 } 48 return &MockConn{ 49 t: m.t, 50 stmt: stmt, 51 query: m.query, 52 }, nil 53 } 54 55 type MockRows struct { 56 t *testing.T 57 idx int 58 columns []string 59 values [][]driver.Value 60 } 61 62 func (m *MockRows) Next(dest []driver.Value) error { 63 if m.idx == len(m.values) { 64 return io.EOF 65 } 66 for i := 0; i < len(dest); i++ { 67 dest[i] = m.values[m.idx][i] 68 } 69 m.idx++ 70 return nil 71 } 72 73 func (m MockRows) Close() error { return nil } 74 75 func (m MockRows) Columns() []string { return m.columns } 76 77 type MockTx struct{} 78 79 func (m MockTx) Commit() error { return nil } 80 81 func (m MockTx) Rollback() error { return nil } 82 83 type MockStmt struct { 84 t *testing.T 85 rows *MockRows 86 idx int 87 values [][]driver.Value 88 mockQuery MockQuery 89 } 90 91 func (s MockStmt) Close() error { return nil } 92 93 func (s MockStmt) NumInput() int { 94 if len(s.values) > 0 { 95 return len(s.values[0]) 96 } 97 return 0 98 } 99 100 func (s *MockStmt) Exec(args []driver.Value) (driver.Result, error) { 101 for i, arg := range args { 102 if s.values[s.idx][i] != arg { 103 s.t.Errorf("arg %t != %t", arg, s.values[s.idx][i]) 104 } 105 } 106 s.idx++ 107 return nil, nil 108 } 109 110 func (s MockStmt) Query(args []driver.Value) (driver.Rows, error) { 111 // use the mock query implementation if supplied by the test 112 if s.mockQuery != nil { 113 return s.mockQuery(args) 114 } 115 return s.rows, nil 116 } 117 118 type MockQuery func(args []driver.Value) (driver.Rows, error) 119 120 type MockConn struct { 121 t *testing.T 122 query string 123 stmt *MockStmt 124 } 125 126 func (m MockConn) Prepare(query string) (driver.Stmt, error) { 127 if query != m.query { 128 m.t.Errorf("invalid query: %s != %s", query, m.query) 129 } 130 return m.stmt, nil 131 } 132 133 func (c MockConn) Close() error { return nil } 134 func (c MockConn) Begin() (driver.Tx, error) { 135 return &MockTx{}, nil 136 } 137 138 var ( 139 _ driver.Conn = (*MockConn)(nil) 140 _ driver.Rows = (*MockRows)(nil) 141 _ driver.Tx = (*MockTx)(nil) 142 _ driver.Stmt = (*MockStmt)(nil) 143 _ driver.Conn = (*MockConn)(nil) 144 ) 145 146 func TestQFrame_ToSQL(t *testing.T) { 147 dvr := MockDriver{t: t} 148 dvr.query = "INSERT INTO test (COL1,COL2,COL3,COL4) VALUES (?,?,?,?);" 149 dvr.args.values = [][]driver.Value{ 150 {int64(1), 1.1, "one", true}, 151 {int64(2), 2.2, "two", true}, 152 {int64(3), 3.3, "three", false}, 153 } 154 sql.Register("TestToSQL", dvr) 155 db, _ := sql.Open("TestToSQL", "") 156 tx, _ := db.Begin() 157 qf := qframe.New(map[string]interface{}{ 158 "COL1": []int{1, 2, 3}, 159 "COL2": []float64{1.1, 2.2, 3.3}, 160 "COL3": []string{"one", "two", "three"}, 161 "COL4": []bool{true, true, false}, 162 }) 163 assertNotErr(t, qf.ToSQL(tx, qsql.Table("test"))) 164 } 165 166 func TestQFrame_ReadSQL(t *testing.T) { 167 dvr := MockDriver{t: t} 168 dvr.results.columns = []string{"COL1", "COL2", "COL3", "COL4"} 169 dvr.results.values = [][]driver.Value{ 170 {int64(1), 1.1, "one", true}, 171 {int64(2), 2.2, "two", true}, 172 {int64(3), 3.3, "three", false}, 173 } 174 sql.Register("TestReadSQL", dvr) 175 db, _ := sql.Open("TestReadSQL", "") 176 tx, _ := db.Begin() 177 qf := qframe.ReadSQL(tx) 178 assertNotErr(t, qf.Err) 179 expected := qframe.New(map[string]interface{}{ 180 "COL1": []int{1, 2, 3}, 181 "COL2": []float64{1.1, 2.2, 3.3}, 182 "COL3": []string{"one", "two", "three"}, 183 "COL4": []bool{true, true, false}, 184 }) 185 assertEquals(t, expected, qf) 186 } 187 188 func TestQFrame_ReadSQLCoercion(t *testing.T) { 189 dvr := MockDriver{t: t} 190 dvr.results.columns = []string{"COL1", "COL2"} 191 dvr.results.values = [][]driver.Value{ 192 {int64(1), int64(0)}, 193 {int64(1), int64(0)}, 194 {int64(0), int64(1)}, 195 } 196 sql.Register("TestReadSQLCoercion", dvr) 197 db, _ := sql.Open("TestReadSQLCoercion", "") 198 tx, _ := db.Begin() 199 qf := qframe.ReadSQL(tx, qsql.Coerce( 200 qsql.CoercePair{Column: "COL1", Type: qsql.Int64ToBool}, 201 qsql.CoercePair{Column: "COL2", Type: qsql.Int64ToBool}, 202 )) 203 assertNotErr(t, qf.Err) 204 expected := qframe.New(map[string]interface{}{ 205 "COL1": []bool{true, true, false}, 206 "COL2": []bool{false, false, true}, 207 }) 208 assertEquals(t, expected, qf) 209 } 210 211 func TestQFrame_ReadWithArgs(t *testing.T) { 212 dvr := MockDriver{t: t} 213 dvr.args.values = [][]driver.Value{{""}} 214 215 // mock rows indexed by string COL3 216 indexRows := map[string][][]driver.Value{ 217 "one": {{int64(1), 1.1, "one", true}}, 218 "two": {{int64(2), 1.2, "two", false}}, 219 "three": {{int64(3), 1.3, "three", true}}, 220 } 221 dvr.mockQuery = func(args []driver.Value) (driver.Rows, error) { 222 // to confirm our argument made it through to the db driver as expected 223 if len(args) != 1 { 224 t.Error("expecting one argument in query invocation") 225 } 226 val, valid := args[0].(string) 227 if !valid { 228 t.Error("expecting argument in query invocation to be a string") 229 } 230 matching, has := indexRows[val] 231 if !has { 232 matching = [][]driver.Value{} 233 } 234 return &MockRows{ 235 t: t, 236 columns: []string{"COL1", "COL2", "COL3", "COL4"}, 237 values: matching, 238 }, nil 239 } 240 stmt := "SELECT * FROM mock_table WHERE COL3=$1" 241 dvr.query = stmt 242 sql.Register("TestReadPrepared", dvr) 243 db, _ := sql.Open("TestReadPrepared", "") 244 tx, _ := db.Begin() 245 qf := qframe.ReadSQLWithArgs(tx, []interface{}{"one"}, qsql.Query(stmt)) 246 assertNotErr(t, qf.Err) 247 expected := qframe.New(map[string]interface{}{ 248 "COL1": []int{1}, 249 "COL2": []float64{1.1}, 250 "COL3": []string{"one"}, 251 "COL4": []bool{true}, 252 }) 253 assertEquals(t, expected, qf) 254 }