SQLStore: Run tests as integration tests (#28265)

* sqlstore: Run tests as integration tests

* Truncate database instead of re-creating it on each test

* Fix test description

See https://github.com/grafana/grafana/pull/12129

* Fix lint issues

* Fix postgres dialect after review suggestion

* Rename and document functions after review suggestion

* Add periods

* Fix auto-increment value for mysql dialect

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
pull/28299/head^2
Sofia Papagiannaki 5 years ago committed by GitHub
parent c07896063b
commit 4937f0daab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      pkg/services/sqlstore/alert_notification_test.go
  2. 4
      pkg/services/sqlstore/alert_test.go
  3. 2
      pkg/services/sqlstore/annotation_test.go
  4. 2
      pkg/services/sqlstore/apikey_test.go
  5. 2
      pkg/services/sqlstore/dashboard_acl_test.go
  6. 2
      pkg/services/sqlstore/dashboard_folder_test.go
  7. 2
      pkg/services/sqlstore/dashboard_provisioning_test.go
  8. 2
      pkg/services/sqlstore/dashboard_service_integration_test.go
  9. 2
      pkg/services/sqlstore/dashboard_snapshot_test.go
  10. 2
      pkg/services/sqlstore/dashboard_test.go
  11. 2
      pkg/services/sqlstore/dashboard_version_test.go
  12. 2
      pkg/services/sqlstore/datasource_test.go
  13. 2
      pkg/services/sqlstore/login_attempt_test.go
  14. 5
      pkg/services/sqlstore/migrator/dialect.go
  15. 31
      pkg/services/sqlstore/migrator/mysql_dialect.go
  16. 35
      pkg/services/sqlstore/migrator/postgres_dialect.go
  17. 34
      pkg/services/sqlstore/migrator/sqlite_dialect.go
  18. 2
      pkg/services/sqlstore/org_test.go
  19. 2
      pkg/services/sqlstore/playlist_test.go
  20. 2
      pkg/services/sqlstore/preferences_test.go
  21. 2
      pkg/services/sqlstore/quota_test.go
  22. 2
      pkg/services/sqlstore/searchstore/search_test.go
  23. 2
      pkg/services/sqlstore/sqlbuilder_test.go
  24. 108
      pkg/services/sqlstore/sqlstore.go
  25. 2
      pkg/services/sqlstore/sqlstore_test.go
  26. 2
      pkg/services/sqlstore/stars_test.go
  27. 2
      pkg/services/sqlstore/stats_test.go
  28. 2
      pkg/services/sqlstore/tags_test.go
  29. 2
      pkg/services/sqlstore/team_test.go
  30. 2
      pkg/services/sqlstore/temp_user_test.go
  31. 2
      pkg/services/sqlstore/transactions_test.go
  32. 2
      pkg/services/sqlstore/user_auth_test.go
  33. 2
      pkg/services/sqlstore/user_test.go

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (
@ -128,7 +130,7 @@ func TestAlertingDataAccess(t *testing.T) {
So(alert.DashboardSlug, ShouldEqual, "dashboard-with-alerts") So(alert.DashboardSlug, ShouldEqual, "dashboard-with-alerts")
}) })
Convey("Viewer cannot read alerts", func() { Convey("Viewer can read alerts", func() {
viewerUser := &models.SignedInUser{OrgRole: models.ROLE_VIEWER, OrgId: 1} viewerUser := &models.SignedInUser{OrgRole: models.ROLE_VIEWER, OrgId: 1}
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: viewerUser} alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: viewerUser}
err2 := HandleAlertsQuery(&alertQuery) err2 := HandleAlertsQuery(&alertQuery)

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -45,6 +45,7 @@ type Dialect interface {
PostInsertId(table string, sess *xorm.Session) error PostInsertId(table string, sess *xorm.Session) error
CleanDB() error CleanDB() error
TruncateDBTables() error
NoOpSql() string NoOpSql() string
IsUniqueConstraintViolation(err error) bool IsUniqueConstraintViolation(err error) bool
@ -270,3 +271,7 @@ func (db *BaseDialect) CleanDB() error {
func (db *BaseDialect) NoOpSql() string { func (db *BaseDialect) NoOpSql() string {
return "SELECT 0;" return "SELECT 0;"
} }
func (db *BaseDialect) TruncateDBTables() error {
return nil
}

@ -1,6 +1,7 @@
package migrator package migrator
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
@ -137,6 +138,36 @@ func (db *Mysql) CleanDB() error {
return nil return nil
} }
// TruncateDBTables truncates all the tables.
// A special case is the dashboard_acl table where we keep the default permissions.
func (db *Mysql) TruncateDBTables() error {
tables, err := db.engine.DBMetas()
if err != nil {
return err
}
sess := db.engine.NewSession()
defer sess.Close()
for _, table := range tables {
switch table.Name {
case "dashboard_acl":
// keep default dashboard permissions
if _, err := sess.Exec(fmt.Sprintf("DELETE FROM %v WHERE dashboard_id != -1 AND org_id != -1;", db.Quote(table.Name))); err != nil {
return errutil.Wrapf(err, "failed to truncate table %q", table.Name)
}
if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE %v AUTO_INCREMENT = 3;", db.Quote(table.Name))); err != nil {
return errutil.Wrapf(err, "failed to reset table %q", table.Name)
}
default:
if _, err := sess.Exec(fmt.Sprintf("TRUNCATE TABLE %v;", db.Quote(table.Name))); err != nil {
return errutil.Wrapf(err, "failed to truncate table %q", table.Name)
}
}
}
return nil
}
func (db *Mysql) isThisError(err error, errcode uint16) bool { func (db *Mysql) isThisError(err error, errcode uint16) bool {
if driverErr, ok := err.(*mysql.MySQLError); ok { if driverErr, ok := err.(*mysql.MySQLError); ok {
if driverErr.Number == errcode { if driverErr.Number == errcode {

@ -140,6 +140,37 @@ func (db *Postgres) CleanDB() error {
return nil return nil
} }
// TruncateDBTables truncates all the tables.
// A special case is the dashboard_acl table where we keep the default permissions.
func (db *Postgres) TruncateDBTables() error {
sess := db.engine.NewSession()
defer sess.Close()
for _, table := range db.engine.Tables {
switch table.Name {
case "":
continue
case "dashboard_acl":
// keep default dashboard permissions
if _, err := sess.Exec(fmt.Sprintf("DELETE FROM %v WHERE dashboard_id != -1 AND org_id != -1;", db.Quote(table.Name))); err != nil {
return errutil.Wrapf(err, "failed to truncate table %q", table.Name)
}
if _, err := sess.Exec(fmt.Sprintf("ALTER SEQUENCE %v RESTART WITH 3;", db.Quote(fmt.Sprintf("%v_id_seq", table.Name)))); err != nil {
return errutil.Wrapf(err, "failed to reset table %q", table.Name)
}
default:
if _, err := sess.Exec(fmt.Sprintf("TRUNCATE TABLE %v RESTART IDENTITY CASCADE;", db.Quote(table.Name))); err != nil {
if db.isUndefinedTable(err) {
continue
}
return errutil.Wrapf(err, "failed to truncate table %q", table.Name)
}
}
}
return nil
}
func (db *Postgres) isThisError(err error, errcode string) bool { func (db *Postgres) isThisError(err error, errcode string) bool {
if driverErr, ok := err.(*pq.Error); ok { if driverErr, ok := err.(*pq.Error); ok {
if string(driverErr.Code) == errcode { if string(driverErr.Code) == errcode {
@ -157,6 +188,10 @@ func (db *Postgres) ErrorMessage(err error) string {
return "" return ""
} }
func (db *Postgres) isUndefinedTable(err error) bool {
return db.isThisError(err, "42P01")
}
func (db *Postgres) IsUniqueConstraintViolation(err error) bool { func (db *Postgres) IsUniqueConstraintViolation(err error) bool {
return db.isThisError(err, "23505") return db.isThisError(err, "23505")
} }

@ -3,6 +3,7 @@ package migrator
import ( import (
"fmt" "fmt"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
"xorm.io/xorm" "xorm.io/xorm"
) )
@ -85,6 +86,39 @@ func (db *Sqlite3) CleanDB() error {
return nil return nil
} }
// TruncateDBTables deletes all data from all the tables and resets the sequences.
// A special case is the dashboard_acl table where we keep the default permissions.
func (db *Sqlite3) TruncateDBTables() error {
tables, err := db.engine.DBMetas()
if err != nil {
return err
}
sess := db.engine.NewSession()
defer sess.Close()
for _, table := range tables {
switch table.Name {
case "dashboard_acl":
// keep default dashboard permissions
if _, err := sess.Exec(fmt.Sprintf("DELETE FROM %q WHERE dashboard_id != -1 AND org_id != -1;", table.Name)); err != nil {
return errutil.Wrapf(err, "failed to truncate table %q", table.Name)
}
if _, err := sess.Exec("UPDATE sqlite_sequence SET seq = 2 WHERE name = '%s';", table.Name); err != nil {
return errutil.Wrapf(err, "failed to cleanup sqlite_sequence")
}
default:
if _, err := sess.Exec(fmt.Sprintf("DELETE FROM %s;", table.Name)); err != nil {
return errutil.Wrapf(err, "failed to truncate table %q", table.Name)
}
}
}
if _, err := sess.Exec("UPDATE sqlite_sequence SET seq = 0 WHERE name != 'dashboard_acl';"); err != nil {
return errutil.Wrapf(err, "failed to cleanup sqlite_sequence")
}
return nil
}
func (db *Sqlite3) isThisError(err error, errcode int) bool { func (db *Sqlite3) isThisError(err error, errcode int) bool {
if driverErr, ok := err.(sqlite3.Error); ok { if driverErr, ok := err.(sqlite3.Error); ok {
if int(driverErr.ExtendedCode) == errcode { if int(driverErr.ExtendedCode) == errcode {

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
// package search_test contains integration tests for search // package search_test contains integration tests for search
package searchstore_test package searchstore_test

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -340,72 +340,80 @@ type ITestDB interface {
Logf(format string, args ...interface{}) Logf(format string, args ...interface{})
} }
var testSqlStore *SqlStore
// InitTestDB initializes the test DB. // InitTestDB initializes the test DB.
func InitTestDB(t ITestDB) *SqlStore { func InitTestDB(t ITestDB) *SqlStore {
t.Helper() t.Helper()
sqlstore := &SqlStore{} if testSqlStore == nil {
sqlstore.Bus = bus.New() testSqlStore = &SqlStore{}
sqlstore.CacheService = localcache.New(5*time.Minute, 10*time.Minute) testSqlStore.Bus = bus.New()
sqlstore.skipEnsureDefaultOrgAndUser = true testSqlStore.CacheService = localcache.New(5*time.Minute, 10*time.Minute)
testSqlStore.skipEnsureDefaultOrgAndUser = true
dbType := migrator.SQLITE
dbType := migrator.SQLITE
// environment variable present for test db?
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present { // environment variable present for test db?
t.Logf("Using database type %q", db) if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present {
dbType = db t.Logf("Using database type %q", db)
} dbType = db
}
// set test db config
sqlstore.Cfg = setting.NewCfg()
sec, err := sqlstore.Cfg.Raw.NewSection("database")
if err != nil {
t.Fatalf("Failed to create section: %s", err)
}
if _, err := sec.NewKey("type", dbType); err != nil {
t.Fatalf("Failed to create key: %s", err)
}
switch dbType { // set test db config
case "mysql": testSqlStore.Cfg = setting.NewCfg()
if _, err := sec.NewKey("connection_string", sqlutil.MySQLTestDB().ConnStr); err != nil { sec, err := testSqlStore.Cfg.Raw.NewSection("database")
t.Fatalf("Failed to create key: %s", err) if err != nil {
t.Fatalf("Failed to create section: %s", err)
} }
case "postgres": if _, err := sec.NewKey("type", dbType); err != nil {
if _, err := sec.NewKey("connection_string", sqlutil.PostgresTestDB().ConnStr); err != nil {
t.Fatalf("Failed to create key: %s", err) t.Fatalf("Failed to create key: %s", err)
} }
default:
if _, err := sec.NewKey("connection_string", sqlutil.Sqlite3TestDB().ConnStr); err != nil { switch dbType {
t.Fatalf("Failed to create key: %s", err) case "mysql":
if _, err := sec.NewKey("connection_string", sqlutil.MySQLTestDB().ConnStr); err != nil {
t.Fatalf("Failed to create key: %s", err)
}
case "postgres":
if _, err := sec.NewKey("connection_string", sqlutil.PostgresTestDB().ConnStr); err != nil {
t.Fatalf("Failed to create key: %s", err)
}
default:
if _, err := sec.NewKey("connection_string", sqlutil.Sqlite3TestDB().ConnStr); err != nil {
t.Fatalf("Failed to create key: %s", err)
}
} }
}
// need to get engine to clean db before we init // need to get engine to clean db before we init
t.Logf("Creating database connection: %q", sec.Key("connection_string")) t.Logf("Creating database connection: %q", sec.Key("connection_string"))
engine, err := xorm.NewEngine(dbType, sec.Key("connection_string").String()) engine, err := xorm.NewEngine(dbType, sec.Key("connection_string").String())
if err != nil { if err != nil {
t.Fatalf("Failed to init test database: %v", err) t.Fatalf("Failed to init test database: %v", err)
} }
sqlstore.Dialect = migrator.NewDialect(engine) testSqlStore.Dialect = migrator.NewDialect(engine)
// temp global var until we get rid of global vars // temp global var until we get rid of global vars
dialect = sqlstore.Dialect dialect = testSqlStore.Dialect
t.Logf("Cleaning DB") t.Logf("Cleaning DB")
if err := dialect.CleanDB(); err != nil { if err := dialect.CleanDB(); err != nil {
t.Fatalf("Failed to clean test db %v", err) t.Fatalf("Failed to clean test db %v", err)
} }
if err := sqlstore.Init(); err != nil { if err := testSqlStore.Init(); err != nil {
t.Fatalf("Failed to init test database: %v", err) t.Fatalf("Failed to init test database: %v", err)
}
testSqlStore.engine.DatabaseTZ = time.UTC
testSqlStore.engine.TZLocation = time.UTC
} }
sqlstore.engine.DatabaseTZ = time.UTC if err := dialect.TruncateDBTables(); err != nil {
sqlstore.engine.TZLocation = time.UTC t.Fatalf("Failed to truncate test db %v", err)
}
return sqlstore return testSqlStore
} }
func IsTestDbMySql() bool { func IsTestDbMySql() bool {

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

@ -1,3 +1,5 @@
// +build integration
package sqlstore package sqlstore
import ( import (

Loading…
Cancel
Save