package repository import ( "database/sql" "fmt" "log" "regexp" "testing" "github.com/DATA-DOG/go-sqlmock" "github.com/stretchr/testify/assert" "gorm.io/driver/postgres" "gorm.io/gorm" ) func NewMockDB() (*sql.DB, *gorm.DB, sqlmock.Sqlmock) { sqldb, mock, err := sqlmock.New() if err != nil { log.Fatalf("An error '%s' was not expected when opening a stub database connection", err) } gormdb, err := gorm.Open(postgres.New(postgres.Config{ WithoutQuotingCheck: true, Conn: sqldb, }), &gorm.Config{}) if err != nil { log.Fatalf("An error '%s' was not expected when opening gorm database", err) } return sqldb, gormdb, mock } type MyModel struct { Id int Value string Count int } func (m MyModel) TableName() string { return "my_models" } type MyModelFilter struct { Id *int `filterfield:"field=id,operator=EQ"` Value *string `filterfield:"field=value,operator=EQ"` Count *int `filterfield:"field=count,operator=GT"` } func TestListMethod(t *testing.T) { t.Run("With ordering", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) filter := MyModelFilter{} ordering := []Order{ { Field: "id", Direction: OrderASC, }, { Field: "count", Direction: OrderDESC, }, } sql := "SELECT * FROM my_models ORDER BY id,count DESC" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))) _, err := repo.List(filter, &ordering, nil) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) t.Run("With limit", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) filter := MyModelFilter{} pagination := Pagination{ Limit: 111, Offset: 0, } sql := "SELECT * FROM my_models LIMIT $1" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))). WithArgs(pagination.Limit) _, err := repo.List(filter, nil, &pagination) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) t.Run("With offset", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) filter := MyModelFilter{} pagination := Pagination{ Limit: 0, Offset: 222, } sql := "SELECT * FROM my_models OFFSET $1" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))). WithArgs(pagination.Offset) _, err := repo.List(filter, nil, &pagination) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) t.Run("With limit and offset", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) filter := MyModelFilter{} pagination := Pagination{ Limit: 111, Offset: 222, } sql := "SELECT * FROM my_models LIMIT $1 OFFSET $2" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))). WithArgs(pagination.Limit, pagination.Offset) _, err := repo.List(filter, nil, &pagination) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) t.Run("Simple filter", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) id := 123 filter := MyModelFilter{ Id: &id, } sql := "SELECT * FROM my_models WHERE my_models.id = $1" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))). WithArgs(id) _, err := repo.List(filter, nil, nil) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) t.Run("Multiple filter values", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) id := 123 count := 456 value := "some value" filter := MyModelFilter{ Id: &id, Value: &value, Count: &count, } sql := "SELECT * FROM my_models WHERE my_models.id = $1 AND my_models.value = $2 AND my_models.count > $3" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))). WithArgs(id, value, count) _, err := repo.List(filter, nil, nil) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) t.Run("Multiple filter values and pagination", func(t *testing.T) { sqldb, db, mock := NewMockDB() defer sqldb.Close() repo := RepoBase[MyModel]{} repo.Init(db) id := 123 count := 456 value := "some value" filter := MyModelFilter{ Id: &id, Value: &value, Count: &count, } pagination := Pagination{ Offset: 111, Limit: 222, } sql := "SELECT * FROM my_models WHERE my_models.id = $1 AND my_models.value = $2 AND my_models.count > $3 LIMIT $4 OFFSET $5" mock.ExpectQuery(fmt.Sprintf("^%s$", regexp.QuoteMeta(sql))). WithArgs(id, value, count, pagination.Limit, pagination.Offset) _, err := repo.List(filter, nil, &pagination) assert.Nil(t, err) if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("there were unfulfilled expectations: %s", err) } }) }