mirror of https://github.com/VinGarcia/ksql.git
1010 lines
25 KiB
Go
1010 lines
25 KiB
Go
package kissorm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ditointernet/go-assert"
|
|
"github.com/jinzhu/gorm"
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
|
_ "github.com/lib/pq"
|
|
"github.com/vingarcia/kissorm/nullable"
|
|
)
|
|
|
|
type User struct {
|
|
ID uint `gorm:"id"`
|
|
Name string `gorm:"name"`
|
|
Age int `gorm:"age"`
|
|
CreatedAt time.Time `gorm:"created_at"`
|
|
}
|
|
|
|
func TestQuery(t *testing.T) {
|
|
for _, driver := range []string{"sqlite3", "postgres"} {
|
|
t.Run(driver, func(t *testing.T) {
|
|
err := createTable(driver)
|
|
if err != nil {
|
|
t.Fatal("could not create test table!")
|
|
}
|
|
|
|
t.Run("should return 0 results correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
var users []User
|
|
err := c.Query(ctx, &users, `SELECT * FROM users WHERE id=1;`)
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, []User(nil), users)
|
|
|
|
users = []User{}
|
|
err = c.Query(ctx, &users, `SELECT * FROM users WHERE id=1;`)
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, []User{}, users)
|
|
})
|
|
|
|
t.Run("should return a user correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
db.Create(&User{
|
|
Name: "Bia",
|
|
})
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
var users []User
|
|
err = c.Query(ctx, &users, `SELECT * FROM users WHERE name=`+c.dialect.Placeholder(0), "Bia")
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 1, len(users))
|
|
assert.Equal(t, "Bia", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
})
|
|
|
|
t.Run("should return multiple users correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
db.Create(&User{
|
|
Name: "João Garcia",
|
|
})
|
|
|
|
db.Create(&User{
|
|
Name: "Bia Garcia",
|
|
})
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
var users []User
|
|
err = c.Query(ctx, &users, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Garcia")
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 2, len(users))
|
|
assert.Equal(t, "João Garcia", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
assert.Equal(t, "Bia Garcia", users[1].Name)
|
|
assert.NotEqual(t, uint(0), users[1].ID)
|
|
})
|
|
|
|
t.Run("should report error if input is not a pointer to a slice of structs", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
db.Create(&User{
|
|
Name: "Andréa Sá",
|
|
})
|
|
|
|
db.Create(&User{
|
|
Name: "Caio Sá",
|
|
})
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "postgres", "users")
|
|
err = c.Query(ctx, &User{}, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Sá")
|
|
assert.NotEqual(t, nil, err)
|
|
|
|
err = c.Query(ctx, []User{}, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Sá")
|
|
assert.NotEqual(t, nil, err)
|
|
|
|
var i int
|
|
err = c.Query(ctx, &i, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Sá")
|
|
assert.NotEqual(t, nil, err)
|
|
|
|
err = c.Query(ctx, &[]int{}, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Sá")
|
|
assert.NotEqual(t, nil, err)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQueryOne(t *testing.T) {
|
|
for _, driver := range []string{"sqlite3", "postgres"} {
|
|
t.Run(driver, func(t *testing.T) {
|
|
err := createTable(driver)
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
t.Run("should return RecordNotFoundErr when there are no results", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "postgres", "users")
|
|
u := User{}
|
|
err := c.QueryOne(ctx, &u, `SELECT * FROM users WHERE id=1;`)
|
|
assert.Equal(t, ErrRecordNotFound, err)
|
|
})
|
|
|
|
t.Run("should return a user correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
db.Create(&User{
|
|
Name: "Bia",
|
|
})
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "postgres", "users")
|
|
u := User{}
|
|
err = c.QueryOne(ctx, &u, `SELECT * FROM users WHERE name=`+c.dialect.Placeholder(0), "Bia")
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, "Bia", u.Name)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
})
|
|
|
|
t.Run("should report error if input is not a pointer to struct", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
db.Create(&User{
|
|
Name: "Andréa Sá",
|
|
})
|
|
|
|
db.Create(&User{
|
|
Name: "Caio Sá",
|
|
})
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "postgres", "users")
|
|
|
|
err = c.QueryOne(ctx, &[]User{}, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Sá")
|
|
assert.NotEqual(t, nil, err)
|
|
|
|
err = c.QueryOne(ctx, User{}, `SELECT * FROM users WHERE name like `+c.dialect.Placeholder(0), "% Sá")
|
|
assert.NotEqual(t, nil, err)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInsert(t *testing.T) {
|
|
for _, driver := range []string{"sqlite3", "postgres"} {
|
|
t.Run(driver, func(t *testing.T) {
|
|
err := createTable(driver)
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
t.Run("should ignore empty lists of users", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
err = c.Insert(ctx)
|
|
assert.Equal(t, nil, err)
|
|
})
|
|
|
|
t.Run("should insert one user correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
u := User{
|
|
Name: "Fernanda",
|
|
}
|
|
|
|
err := c.Insert(ctx, &u)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, 0, u.ID)
|
|
|
|
result := User{}
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, u.Name, result.Name)
|
|
assert.Equal(t, u.CreatedAt.Format(time.RFC3339), result.CreatedAt.Format(time.RFC3339))
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
t.Run("should ignore empty lists of ids", func(t *testing.T) {
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
u := User{
|
|
Name: "Won't be deleted",
|
|
}
|
|
|
|
err := c.Insert(ctx, &u)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
|
|
result := User{}
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u.ID, result.ID)
|
|
|
|
err = c.Delete(ctx)
|
|
assert.Equal(t, nil, err)
|
|
|
|
result = User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u.ID, result.ID)
|
|
})
|
|
|
|
t.Run("should delete one id correctly", func(t *testing.T) {
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
u1 := User{
|
|
Name: "Fernanda",
|
|
}
|
|
|
|
err := c.Insert(ctx, &u1)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u1.ID)
|
|
|
|
result := User{}
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u1.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u1.ID, result.ID)
|
|
|
|
u2 := User{
|
|
Name: "Won't be deleted",
|
|
}
|
|
|
|
err = c.Insert(ctx, &u2)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u2.ID)
|
|
|
|
result = User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id=?", u2.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u2.ID, result.ID)
|
|
|
|
err = c.Delete(ctx, u1.ID)
|
|
assert.Equal(t, nil, err)
|
|
|
|
result = User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id=?", u1.ID)
|
|
it.Scan(&result)
|
|
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, uint(0), result.ID)
|
|
assert.Equal(t, "", result.Name)
|
|
|
|
result = User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id=?", u2.ID)
|
|
it.Scan(&result)
|
|
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.NotEqual(t, uint(0), result.ID)
|
|
assert.Equal(t, "Won't be deleted", result.Name)
|
|
})
|
|
|
|
t.Run("should delete multiple ids correctly", func(t *testing.T) {
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
u1 := User{
|
|
Name: "Fernanda",
|
|
}
|
|
err := c.Insert(ctx, &u1)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u1.ID)
|
|
|
|
u2 := User{
|
|
Name: "Juliano",
|
|
}
|
|
err = c.Insert(ctx, &u2)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u2.ID)
|
|
|
|
u3 := User{
|
|
Name: "This won't be deleted",
|
|
}
|
|
err = c.Insert(ctx, &u3)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u3.ID)
|
|
|
|
result := User{}
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u1.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u1.ID, result.ID)
|
|
|
|
result = User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id=?", u2.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u2.ID, result.ID)
|
|
|
|
result = User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id=?", u3.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, u3.ID, result.ID)
|
|
|
|
err = c.Delete(ctx, u1.ID, u2.ID)
|
|
assert.Equal(t, nil, err)
|
|
|
|
results := []User{}
|
|
it = c.db.Raw("SELECT * FROM users WHERE id IN (?, ?, ?)", u1.ID, u2.ID, u3.ID)
|
|
it.Scan(&results)
|
|
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, 1, len(results))
|
|
assert.Equal(t, "This won't be deleted", results[0].Name)
|
|
})
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
for _, driver := range []string{"sqlite3", "postgres"} {
|
|
t.Run(driver, func(t *testing.T) {
|
|
err := createTable(driver)
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
t.Run("should ignore empty lists of ids", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
u := User{
|
|
Name: "Thay",
|
|
}
|
|
err := c.Insert(ctx, &u)
|
|
assert.Equal(t, nil, err)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
|
|
// Empty update, should do nothing:
|
|
err = c.Update(ctx)
|
|
assert.Equal(t, nil, err)
|
|
|
|
result := User{}
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
it.Close()
|
|
assert.Equal(t, nil, err)
|
|
|
|
assert.Equal(t, "Thay", result.Name)
|
|
})
|
|
|
|
t.Run("should update one user correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
u := User{
|
|
Name: "Letícia",
|
|
}
|
|
r := c.db.Table(c.tableName).Create(&u)
|
|
assert.Equal(t, nil, r.Error)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
|
|
err = c.Update(ctx, User{
|
|
ID: u.ID,
|
|
Name: "Thayane",
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
|
|
var result User
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, "Thayane", result.Name)
|
|
})
|
|
|
|
t.Run("should update one user correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
u := User{
|
|
Name: "Letícia",
|
|
}
|
|
r := c.db.Table(c.tableName).Create(&u)
|
|
assert.Equal(t, nil, r.Error)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
|
|
err = c.Update(ctx, User{
|
|
ID: u.ID,
|
|
Name: "Thayane",
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
|
|
var result User
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, "Thayane", result.Name)
|
|
})
|
|
|
|
t.Run("should ignore null pointers on partial updates", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
type partialUser struct {
|
|
ID uint `gorm:"id"`
|
|
Name string `gorm:"name"`
|
|
Age *int `gorm:"age"`
|
|
}
|
|
u := partialUser{
|
|
Name: "Letícia",
|
|
Age: nullable.Int(22),
|
|
}
|
|
r := c.db.Table(c.tableName).Create(&u)
|
|
assert.Equal(t, nil, r.Error)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
|
|
err = c.Update(ctx, partialUser{
|
|
ID: u.ID,
|
|
// Should be updated because it is not null, just empty:
|
|
Name: "",
|
|
// Should not be updated because it is null:
|
|
Age: nil,
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
|
|
var result User
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, "", result.Name)
|
|
assert.Equal(t, 22, result.Age)
|
|
})
|
|
|
|
t.Run("should update valid pointers on partial updates", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "users")
|
|
|
|
type partialUser struct {
|
|
ID uint `gorm:"id"`
|
|
Name string `gorm:"name"`
|
|
Age *int `gorm:"age"`
|
|
}
|
|
u := partialUser{
|
|
Name: "Letícia",
|
|
Age: nullable.Int(22),
|
|
}
|
|
r := c.db.Table(c.tableName).Create(&u)
|
|
assert.Equal(t, nil, r.Error)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
|
|
// Should update all fields:
|
|
err = c.Update(ctx, partialUser{
|
|
ID: u.ID,
|
|
Name: "Thay",
|
|
Age: nullable.Int(42),
|
|
})
|
|
assert.Equal(t, nil, err)
|
|
|
|
var result User
|
|
it := c.db.Raw("SELECT * FROM users WHERE id=?", u.ID)
|
|
it.Scan(&result)
|
|
assert.Equal(t, nil, it.Error)
|
|
assert.Equal(t, "Thay", result.Name)
|
|
assert.Equal(t, 42, result.Age)
|
|
})
|
|
|
|
t.Run("should report database errors correctly", func(t *testing.T) {
|
|
db := connectDB(t, driver)
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, driver, "non_existing_table")
|
|
|
|
err = c.Update(ctx, User{
|
|
ID: 1,
|
|
Name: "Thayane",
|
|
})
|
|
assert.NotEqual(t, nil, err)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStructToMap(t *testing.T) {
|
|
type S1 struct {
|
|
Name string `gorm:"name_attr"`
|
|
Age int `gorm:"age_attr"`
|
|
}
|
|
t.Run("should convert plain structs to maps", func(t *testing.T) {
|
|
m, err := StructToMap(S1{
|
|
Name: "my name",
|
|
Age: 22,
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, map[string]interface{}{
|
|
"name_attr": "my name",
|
|
"age_attr": 22,
|
|
}, m)
|
|
})
|
|
|
|
t.Run("should not ignore zero value attrs, if they are not pointers", func(t *testing.T) {
|
|
m, err := StructToMap(S1{
|
|
Name: "",
|
|
Age: 0,
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, map[string]interface{}{
|
|
"name_attr": "",
|
|
"age_attr": 0,
|
|
}, m)
|
|
})
|
|
|
|
type S2 struct {
|
|
Name *string `gorm:"name"`
|
|
Age *int `gorm:"age"`
|
|
}
|
|
|
|
t.Run("should not ignore not nil pointers", func(t *testing.T) {
|
|
str := ""
|
|
age := 0
|
|
m, err := StructToMap(S2{
|
|
Name: &str,
|
|
Age: &age,
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, map[string]interface{}{
|
|
"name": "",
|
|
"age": 0,
|
|
}, m)
|
|
})
|
|
|
|
t.Run("should ignore nil pointers", func(t *testing.T) {
|
|
m, err := StructToMap(S2{
|
|
Name: nil,
|
|
Age: nil,
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, map[string]interface{}{}, m)
|
|
})
|
|
}
|
|
|
|
func TestQueryChunks(t *testing.T) {
|
|
t.Run("should query a single row correctly", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
_ = c.Insert(ctx, &User{Name: "User1"})
|
|
|
|
var length int
|
|
var u User
|
|
err = c.QueryChunks(ctx, ChunkParser{
|
|
Query: `select * from users where name = ?;`,
|
|
Params: []interface{}{"User1"},
|
|
|
|
ChunkSize: 100,
|
|
ForEachChunk: func(users []User) error {
|
|
length = len(users)
|
|
if length > 0 {
|
|
u = users[0]
|
|
}
|
|
return nil
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 1, length)
|
|
assert.NotEqual(t, uint(0), u.ID)
|
|
assert.Equal(t, "User1", u.Name)
|
|
})
|
|
|
|
t.Run("should query one chunk correctly", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
_ = c.Insert(ctx, &User{Name: "User1"})
|
|
_ = c.Insert(ctx, &User{Name: "User2"})
|
|
|
|
var lengths []int
|
|
var users []User
|
|
err = c.QueryChunks(ctx, ChunkParser{
|
|
Query: `select * from users where name like ? order by name asc;`,
|
|
Params: []interface{}{"User%"},
|
|
|
|
ChunkSize: 2,
|
|
ForEachChunk: func(buffer []User) error {
|
|
users = append(users, buffer...)
|
|
lengths = append(lengths, len(buffer))
|
|
return nil
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 1, len(lengths))
|
|
assert.Equal(t, 2, lengths[0])
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
assert.Equal(t, "User1", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[1].ID)
|
|
assert.Equal(t, "User2", users[1].Name)
|
|
})
|
|
|
|
t.Run("should query chunks of 1 correctly", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
_ = c.Insert(ctx, &User{Name: "User1"})
|
|
_ = c.Insert(ctx, &User{Name: "User2"})
|
|
|
|
var lengths []int
|
|
var users []User
|
|
err = c.QueryChunks(ctx, ChunkParser{
|
|
Query: `select * from users where name like ? order by name asc;`,
|
|
Params: []interface{}{"User%"},
|
|
|
|
ChunkSize: 1,
|
|
ForEachChunk: func(buffer []User) error {
|
|
lengths = append(lengths, len(buffer))
|
|
users = append(users, buffer...)
|
|
return nil
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 2, len(users))
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
assert.Equal(t, "User1", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[1].ID)
|
|
assert.Equal(t, "User2", users[1].Name)
|
|
assert.Equal(t, []int{1, 1}, lengths)
|
|
})
|
|
|
|
t.Run("should load partially filled chunks correctly", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
_ = c.Insert(ctx, &User{Name: "User1"})
|
|
_ = c.Insert(ctx, &User{Name: "User2"})
|
|
_ = c.Insert(ctx, &User{Name: "User3"})
|
|
|
|
var lengths []int
|
|
var users []User
|
|
err = c.QueryChunks(ctx, ChunkParser{
|
|
Query: `select * from users where name like ? order by name asc;`,
|
|
Params: []interface{}{"User%"},
|
|
|
|
ChunkSize: 2,
|
|
ForEachChunk: func(buffer []User) error {
|
|
lengths = append(lengths, len(buffer))
|
|
users = append(users, buffer...)
|
|
return nil
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 3, len(users))
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
assert.Equal(t, "User1", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[1].ID)
|
|
assert.Equal(t, "User2", users[1].Name)
|
|
assert.NotEqual(t, uint(0), users[2].ID)
|
|
assert.Equal(t, "User3", users[2].Name)
|
|
assert.Equal(t, []int{2, 1}, lengths)
|
|
})
|
|
|
|
t.Run("should abort the first iteration when the callback returns an ErrAbortIteration", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
_ = c.Insert(ctx, &User{Name: "User1"})
|
|
_ = c.Insert(ctx, &User{Name: "User2"})
|
|
_ = c.Insert(ctx, &User{Name: "User3"})
|
|
|
|
var lengths []int
|
|
var users []User
|
|
err = c.QueryChunks(ctx, ChunkParser{
|
|
Query: `select * from users where name like ? order by name asc;`,
|
|
Params: []interface{}{"User%"},
|
|
|
|
ChunkSize: 2,
|
|
ForEachChunk: func(buffer []User) error {
|
|
lengths = append(lengths, len(buffer))
|
|
users = append(users, buffer...)
|
|
return ErrAbortIteration
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 2, len(users))
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
assert.Equal(t, "User1", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[1].ID)
|
|
assert.Equal(t, "User2", users[1].Name)
|
|
assert.Equal(t, []int{2}, lengths)
|
|
})
|
|
|
|
t.Run("should abort the last iteration when the callback returns an ErrAbortIteration", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
ctx := context.Background()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
|
|
_ = c.Insert(ctx, &User{Name: "User1"})
|
|
_ = c.Insert(ctx, &User{Name: "User2"})
|
|
_ = c.Insert(ctx, &User{Name: "User3"})
|
|
|
|
returnVals := []error{nil, ErrAbortIteration}
|
|
var lengths []int
|
|
var users []User
|
|
err = c.QueryChunks(ctx, ChunkParser{
|
|
Query: `select * from users where name like ? order by name asc;`,
|
|
Params: []interface{}{"User%"},
|
|
|
|
ChunkSize: 2,
|
|
ForEachChunk: func(buffer []User) error {
|
|
lengths = append(lengths, len(buffer))
|
|
users = append(users, buffer...)
|
|
|
|
return shiftErrSlice(&returnVals)
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 3, len(users))
|
|
assert.NotEqual(t, uint(0), users[0].ID)
|
|
assert.Equal(t, "User1", users[0].Name)
|
|
assert.NotEqual(t, uint(0), users[1].ID)
|
|
assert.Equal(t, "User2", users[1].Name)
|
|
assert.NotEqual(t, uint(0), users[2].ID)
|
|
assert.Equal(t, "User3", users[2].Name)
|
|
assert.Equal(t, []int{2, 1}, lengths)
|
|
})
|
|
}
|
|
|
|
func TestFillSliceWith(t *testing.T) {
|
|
t.Run("should fill a list correctly", func(t *testing.T) {
|
|
var users []User
|
|
err := FillSliceWith(&users, []map[string]interface{}{
|
|
{
|
|
"name": "Jorge",
|
|
},
|
|
{
|
|
"name": "Luciana",
|
|
},
|
|
{
|
|
"name": "Breno",
|
|
},
|
|
})
|
|
|
|
assert.Equal(t, nil, err)
|
|
assert.Equal(t, 3, len(users))
|
|
assert.Equal(t, "Jorge", users[0].Name)
|
|
assert.Equal(t, "Luciana", users[1].Name)
|
|
assert.Equal(t, "Breno", users[2].Name)
|
|
})
|
|
}
|
|
|
|
func TestScanRows(t *testing.T) {
|
|
t.Run("should scan users correctly", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
ctx := context.TODO()
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
c := newTestClient(db, "sqlite3", "users")
|
|
_ = c.Insert(ctx, &User{Name: "User1", Age: 22})
|
|
_ = c.Insert(ctx, &User{Name: "User2", Age: 14})
|
|
_ = c.Insert(ctx, &User{Name: "User3", Age: 43})
|
|
|
|
rows, err := db.DB().QueryContext(ctx, "select * from users where name='User2'")
|
|
assert.Equal(t, nil, err)
|
|
|
|
assert.Equal(t, true, rows.Next())
|
|
|
|
var u User
|
|
err = scanRows(rows, &u)
|
|
assert.Equal(t, nil, err)
|
|
|
|
assert.Equal(t, "User2", u.Name)
|
|
assert.Equal(t, 14, u.Age)
|
|
})
|
|
|
|
t.Run("should report error for closed rows", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
ctx := context.TODO()
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
rows, err := db.DB().QueryContext(ctx, "select * from users where name='User2'")
|
|
assert.Equal(t, nil, err)
|
|
|
|
var u User
|
|
err = rows.Close()
|
|
assert.Equal(t, nil, err)
|
|
err = scanRows(rows, &u)
|
|
assert.NotEqual(t, nil, err)
|
|
})
|
|
|
|
t.Run("should report if record is not a pointer", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
ctx := context.TODO()
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
rows, err := db.DB().QueryContext(ctx, "select * from users where name='User2'")
|
|
assert.Equal(t, nil, err)
|
|
|
|
var u User
|
|
err = scanRows(rows, u)
|
|
assert.NotEqual(t, nil, err)
|
|
})
|
|
|
|
t.Run("should report if record is not a pointer to struct", func(t *testing.T) {
|
|
err := createTable("sqlite3")
|
|
if err != nil {
|
|
t.Fatal("could not create test table!, reason:", err.Error())
|
|
}
|
|
|
|
ctx := context.TODO()
|
|
db := connectDB(t, "sqlite3")
|
|
defer db.Close()
|
|
|
|
rows, err := db.DB().QueryContext(ctx, "select * from users where name='User2'")
|
|
assert.Equal(t, nil, err)
|
|
|
|
var u map[string]interface{}
|
|
err = scanRows(rows, &u)
|
|
assert.NotEqual(t, nil, err)
|
|
})
|
|
}
|
|
|
|
var connectionString = map[string]string{
|
|
"postgres": "host=localhost port=5432 user=postgres dbname=kissorm sslmode=disable",
|
|
"sqlite3": "/tmp/kissorm.db",
|
|
}
|
|
|
|
func createTable(driver string) error {
|
|
connStr := connectionString[driver]
|
|
if connStr == "" {
|
|
return fmt.Errorf("unsupported driver: '%s'", driver)
|
|
}
|
|
|
|
db, err := gorm.Open(driver, connStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
db.DropTableIfExists(&User{})
|
|
db.CreateTable(&User{})
|
|
|
|
return nil
|
|
}
|
|
|
|
func newTestClient(db *gorm.DB, driver string, tableName string) Client {
|
|
return Client{
|
|
driver: driver,
|
|
dialect: getDriverDialect(driver),
|
|
db: db,
|
|
tableName: tableName,
|
|
}
|
|
}
|
|
|
|
func connectDB(t *testing.T, driver string) *gorm.DB {
|
|
connStr := connectionString[driver]
|
|
if connStr == "" {
|
|
panic(fmt.Sprintf("unsupported driver: '%s'", driver))
|
|
}
|
|
|
|
db, err := gorm.Open(driver, connStr)
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
return db
|
|
}
|
|
|
|
func shiftErrSlice(errs *[]error) error {
|
|
err := (*errs)[0]
|
|
*errs = (*errs)[1:]
|
|
return err
|
|
}
|