diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a7324d1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: go +go: + - '1.8' + - '1.9' + - '1.10' + - '1.11' +before_install: + - go get -t -v ./... +script: + - go test ./... -v -cpu 2 -race -cover -coverprofile=coverage.txt -covermode=atomic +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/core/config.go b/core/config.go index 055b37a..d218df6 100644 --- a/core/config.go +++ b/core/config.go @@ -24,7 +24,6 @@ type ConfigInterface interface { GetVersion() string GetSentryDSN() string GetLogLevel() logging.Level - GetDebug() bool GetHTTPConfig() HTTPServerConfig GetDBConfig() DatabaseConfig GetAWSConfig() ConfigAWS @@ -72,12 +71,12 @@ type ConfigAWS struct { // DatabaseConfig struct type DatabaseConfig struct { - Connection string `yaml:"connection"` - Logging bool `yaml:"logging"` - TablePrefix string `yaml:"table_prefix"` - MaxOpenConnections int `yaml:"max_open_connections"` - MaxIdleConnections int `yaml:"max_idle_connections"` - ConnectionLifetime int `yaml:"connection_lifetime"` + Connection interface{} `yaml:"connection"` + Logging bool `yaml:"logging"` + TablePrefix string `yaml:"table_prefix"` + MaxOpenConnections int `yaml:"max_open_connections"` + MaxIdleConnections int `yaml:"max_idle_connections"` + ConnectionLifetime int `yaml:"connection_lifetime"` } // HTTPServerConfig struct @@ -144,8 +143,8 @@ func (c Config) GetTransportInfo() InfoInterface { return c.TransportInfo } -// GetDebug debug flag -func (c Config) GetDebug() bool { +// IsDebug debug flag +func (c Config) IsDebug() bool { return c.Debug } diff --git a/core/config_test.go b/core/config_test.go index c1b939b..abe0e2d 100644 --- a/core/config_test.go +++ b/core/config_test.go @@ -85,8 +85,8 @@ func (c *ConfigTest) Test_GetLogLevel() { assert.Equal(c.T(), logging.Level(5), c.config.GetLogLevel()) } -func (c *ConfigTest) Test_GetDebug() { - assert.Equal(c.T(), true, c.config.GetDebug()) +func (c *ConfigTest) Test_IsDebug() { + assert.Equal(c.T(), true, c.config.IsDebug()) } func (c *ConfigTest) Test_GetUpdateInterval() { diff --git a/core/engine.go b/core/engine.go index fbb8a04..b09abd2 100644 --- a/core/engine.go +++ b/core/engine.go @@ -36,14 +36,14 @@ func New() *Engine { } func (e *Engine) initGin() { - if !e.Config.GetDebug() { + if !e.Config.IsDebug() { gin.SetMode(gin.ReleaseMode) } r := gin.New() r.Use(gin.Recovery()) - if e.Config.GetDebug() { + if e.Config.IsDebug() { r.Use(gin.Logger()) } @@ -76,7 +76,7 @@ func (e *Engine) Prepare() *Engine { e.LoadTranslations() e.createDB(e.Config.GetDBConfig()) e.createRavenClient(e.Config.GetSentryDSN()) - e.resetUtils(e.Config.GetAWSConfig(), e.Config.GetDebug(), 0) + e.resetUtils(e.Config.GetAWSConfig(), e.Config.IsDebug(), 0) e.Logger = NewLogger(e.Config.GetTransportInfo().GetCode(), e.Config.GetLogLevel(), e.LogFormatter) e.Sentry.Localizer = &e.Localizer e.Utils.Logger = e.Logger diff --git a/core/engine_test.go b/core/engine_test.go new file mode 100644 index 0000000..0d33b43 --- /dev/null +++ b/core/engine_test.go @@ -0,0 +1,160 @@ +package core + +import ( + "database/sql" + "html/template" + "io/ioutil" + "os" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type EngineTest struct { + suite.Suite + engine *Engine +} + +func (e *EngineTest) SetupTest() { + var ( + db *sql.DB + err error + ) + + e.engine = New() + require.NotNil(e.T(), e.engine) + + db, _, err = sqlmock.New() + require.NoError(e.T(), err) + + if _, err := os.Stat(testTranslationsDir); err != nil && os.IsNotExist(err) { + err := os.Mkdir(testTranslationsDir, os.ModePerm) + require.Nil(e.T(), err) + data := []byte("message: Test message\nmessage_template: Test message with {{.data}}") + err = ioutil.WriteFile(testLangFile, data, os.ModePerm) + require.Nil(e.T(), err) + } + + e.engine.Config = Config{ + Version: "1", + LogLevel: 5, + Database: DatabaseConfig{ + Connection: db, + Logging: true, + TablePrefix: "", + MaxOpenConnections: 10, + MaxIdleConnections: 10, + ConnectionLifetime: 60, + }, + SentryDSN: "sentry dsn", + HTTPServer: HTTPServerConfig{ + Host: "0.0.0.0", + Listen: ":3001", + }, + Debug: true, + UpdateInterval: 30, + ConfigAWS: ConfigAWS{}, + TransportInfo: Info{ + Name: "test", + Code: "test", + LogoPath: "/test.svg", + }, + } +} + +func (e *EngineTest) Test_Prepare_Twice() { + defer func() { + r := recover() + require.NotNil(e.T(), r) + assert.Equal(e.T(), "engine already initialized", r.(string)) + }() + + engine := New() + engine.prepared = true + engine.Prepare() +} + +func (e *EngineTest) Test_Prepare_NoConfig() { + defer func() { + r := recover() + require.NotNil(e.T(), r) + assert.Equal(e.T(), "engine.Config must be loaded before initializing", r.(string)) + }() + + engine := New() + engine.prepared = false + engine.Config = nil + engine.Prepare() +} + +func (e *EngineTest) Test_Prepare() { + defer func() { + require.Nil(e.T(), recover()) + }() + + e.engine.TranslationsPath = testTranslationsDir + e.engine.Prepare() + assert.True(e.T(), e.engine.prepared) +} + +func (e *EngineTest) Test_initGin_Release() { + engine := New() + engine.Config = Config{Debug: false} + engine.initGin() + assert.NotNil(e.T(), engine.ginEngine) +} + +func (e *EngineTest) Test_TemplateFuncMap() { + assert.NotNil(e.T(), e.engine.TemplateFuncMap(template.FuncMap{ + "test": func() string { + return "test" + }, + })) +} + +func (e *EngineTest) Test_CreateRenderer() { + e.engine.CreateRenderer(func(r *Renderer) { + assert.NotNil(e.T(), r) + }, template.FuncMap{}) +} + +func (e *EngineTest) Test_Router_Fail() { + defer func() { + r := recover() + require.NotNil(e.T(), r) + assert.Equal(e.T(), "prepare engine first", r.(string)) + }() + + engine := New() + engine.Router() +} + +func (e *EngineTest) Test_Router() { + e.engine.TranslationsPath = testTranslationsDir + e.engine.Prepare() + assert.NotNil(e.T(), e.engine.Router()) +} + +func (e *EngineTest) Test_ConfigureRouter() { + e.engine.TranslationsPath = testTranslationsDir + e.engine.Prepare() + e.engine.ConfigureRouter(func(engine *gin.Engine) { + assert.NotNil(e.T(), engine) + }) +} + +func (e *EngineTest) Test_Run_Fail() { + defer func() { + assert.NotNil(e.T(), recover()) + }() + + _ = New().Run() +} + +func TestEngine_Suite(t *testing.T) { + suite.Run(t, new(EngineTest)) +} diff --git a/core/migrate.go b/core/migrate.go index 3517237..5c7938e 100644 --- a/core/migrate.go +++ b/core/migrate.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "sort" "github.com/jinzhu/gorm" @@ -78,6 +79,10 @@ func (m *Migrate) Rollback() error { return err } + if m.first == nil { + return errors.New("abnormal termination: first migration is nil") + } + if err := m.GORMigrate.RollbackTo(m.first.ID); err == nil { if err := m.GORMigrate.RollbackMigration(m.first); err == nil { return nil @@ -115,10 +120,10 @@ func (m *Migrate) MigrateNextTo(version string) error { if next, err := m.NextFrom(version); err == nil { current := m.Current() switch { - case current > next: - return m.GORMigrate.RollbackTo(next) case current < next: return m.GORMigrate.MigrateTo(next) + case current > next: + return errors.New(fmt.Sprintf("current migration version '%s' is higher than fetched version '%s'", current, next)) default: return nil } @@ -139,7 +144,9 @@ func (m *Migrate) MigratePreviousTo(version string) error { case current > prev: return m.GORMigrate.RollbackTo(prev) case current < prev: - return m.GORMigrate.MigrateTo(prev) + return errors.New(fmt.Sprintf("current migration version '%s' is lower than fetched version '%s'", current, prev)) + case prev == "0": + return m.GORMigrate.RollbackMigration(m.first) default: return nil } @@ -162,17 +169,24 @@ func (m *Migrate) Current() string { var migrationInfo MigrationInfo if m.db == nil { + fmt.Println("warning => db is nil - cannot return migration version") return "0" } if !m.db.HasTable(MigrationInfo{}) { - m.db.CreateTable(MigrationInfo{}) + if err := m.db.CreateTable(MigrationInfo{}).Error; err == nil { + fmt.Println("info => created migrations table") + } else { + panic(err.Error()) + } + return "0" } if err := m.db.Last(&migrationInfo).Error; err == nil { return migrationInfo.ID } else { + fmt.Printf("warning => cannot fetch migration version: %s\n", err.Error()) return "0" } } @@ -199,7 +213,7 @@ func (m *Migrate) PreviousFrom(version string) (string, error) { if key > 0 { return m.versions[key-1], nil } else { - return "", errors.New("this is first migration") + return "0", nil } } } diff --git a/core/migrate_test.go b/core/migrate_test.go index 831d735..8ce67c9 100644 --- a/core/migrate_test.go +++ b/core/migrate_test.go @@ -7,6 +7,7 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/postgres" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -29,13 +30,16 @@ type MigrateTest struct { } func (m *MigrateTest) SetupSuite() { + require.NotEmpty(m.T(), (MigrationInfo{}).TableName()) + m.RefreshMigrate() +} + +func (m *MigrateTest) RefreshMigrate() { var ( db *sql.DB err error ) - require.NotEmpty(m.T(), (MigrationInfo{}).TableName()) - db, m.mock, err = sqlmock.New() require.NoError(m.T(), err) @@ -43,11 +47,6 @@ func (m *MigrateTest) SetupSuite() { require.NoError(m.T(), err) m.DB.LogMode(true) - m.RefreshMigrate() - m.Migrate.SetDB(m.DB) -} - -func (m *MigrateTest) RefreshMigrate() { m.Migrate = &Migrate{ db: m.DB, prepared: false, @@ -55,7 +54,7 @@ func (m *MigrateTest) RefreshMigrate() { } } -func (m *MigrateTest) Migration_TestModel() *gormigrate.Migration { +func (m *MigrateTest) Migration_TestModelFirst() *gormigrate.Migration { return &gormigrate.Migration{ ID: "1", Migrate: func(db *gorm.DB) error { @@ -67,15 +66,28 @@ func (m *MigrateTest) Migration_TestModel() *gormigrate.Migration { } } +func (m *MigrateTest) Migration_TestModelSecond() *gormigrate.Migration { + return &gormigrate.Migration{ + ID: "2", + Migrate: func(db *gorm.DB) error { + return db.Model(TestModel{}).ModifyColumn("name", "varchar(100)").Error + }, + Rollback: func(db *gorm.DB) error { + return db.Model(TestModel{}).ModifyColumn("name", "varchar(70)").Error + }, + } +} + func (m *MigrateTest) Test_Add() { m.RefreshMigrate() m.Migrate.Add(nil) - m.Migrate.Add(m.Migration_TestModel()) + m.Migrate.Add(m.Migration_TestModelFirst()) assert.Equal(m.T(), 1, len(m.Migrate.migrations)) i, ok := m.Migrate.migrations["1"] require.True(m.T(), ok) assert.Equal(m.T(), "1", i.ID) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) } func (m *MigrateTest) Test_prepareMigrations_NilDB() { @@ -85,6 +97,7 @@ func (m *MigrateTest) Test_prepareMigrations_NilDB() { require.Error(m.T(), err) assert.Equal(m.T(), "db must not be nil", err.Error()) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) } func (m *MigrateTest) Test_prepareMigrations_AlreadyPrepared() { @@ -94,21 +107,57 @@ func (m *MigrateTest) Test_prepareMigrations_AlreadyPrepared() { require.NoError(m.T(), err) assert.Nil(m.T(), m.Migrate.GORMigrate) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) } func (m *MigrateTest) Test_prepareMigrations_OK() { m.RefreshMigrate() - m.Migrate.Add(m.Migration_TestModel()) + m.Migrate.Add(m.Migration_TestModelFirst()) err := m.Migrate.prepareMigrations() require.NoError(m.T(), err) assert.True(m.T(), m.Migrate.prepared) assert.NotNil(m.T(), m.Migrate.GORMigrate) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) } -func (m *MigrateTest) Test_Migrate() { +func (m *MigrateTest) Test_Migrate_Fail_NilDB() { m.RefreshMigrate() - m.Migrate.Add(m.Migration_TestModel()) + m.Migrate.SetDB(nil) + m.Migrate.Add(m.Migration_TestModelFirst()) + + err := m.Migrate.Migrate() + + assert.Error(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_Migrate_Success_NoMigrations() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + + m.mock.ExpectBegin() + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE migrations (id VARCHAR(255) PRIMARY KEY)`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT id FROM migrations`)). + WillReturnRows(sqlmock.NewRows([]string{"1"})) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "migrations" WHERE (id = $1)`)). + WithArgs("1"). + WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) + m.mock.ExpectCommit() + + err := m.Migrate.Migrate() + + assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_Migrate_Success() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) m.mock.ExpectBegin() m.mock. @@ -133,6 +182,184 @@ func (m *MigrateTest) Test_Migrate() { err := m.Migrate.Migrate() assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_Rollback_Fail_NilDB() { + m.RefreshMigrate() + m.Migrate.SetDB(nil) + m.Migrate.Add(m.Migration_TestModelFirst()) + + err := m.Migrate.Rollback() + + assert.Error(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_Rollback_Fail_NoMigrations() { + m.RefreshMigrate() + m.Migrate.first = m.Migration_TestModelFirst() + + err := m.Migrate.Rollback() + + assert.Error(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_Rollback_Fail_NoFirstMigration() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + m.Migrate.first = nil + + err := m.Migrate.Rollback() + + assert.Error(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_MigrateTo_Fail_NilDB() { + m.RefreshMigrate() + m.Migrate.SetDB(nil) + + err := m.Migrate.MigrateTo("version") + + assert.Error(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_MigrateTo_DoNothing() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock.ExpectBegin() + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE migrations (id VARCHAR(255) PRIMARY KEY)`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT id FROM migrations`)). + WillReturnRows(sqlmock.NewRows([]string{"1"})) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "migrations" WHERE (id = $1)`)). + WithArgs("1"). + WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) + m.mock.ExpectCommit() + + err := m.Migrate.MigrateTo(m.Migration_TestModelFirst().ID) + + assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_MigrateTo() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock.ExpectBegin() + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE migrations (id VARCHAR(255) PRIMARY KEY)`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT id FROM migrations`)). + WillReturnRows(sqlmock.NewRows([]string{"1"})) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "migrations" WHERE (id = $1)`)). + WithArgs("1"). + WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0)) + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE "test_model" ("name" varchar(70) )`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock. + ExpectExec(regexp.QuoteMeta(`INSERT INTO migrations (id) VALUES ($1)`)). + WithArgs("1"). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock.ExpectCommit() + + err := m.Migrate.MigrateTo(m.Migration_TestModelFirst().ID) + + assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_RollbackTo() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + m.Migrate.Add(m.Migration_TestModelSecond()) + + m.mock.ExpectBegin() + m.mock.ExpectCommit() + + err := m.Migrate.RollbackTo(m.Migration_TestModelSecond().ID) + + assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_MigrateNextTo() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + m.Migrate.Add(m.Migration_TestModelSecond()) + + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock.ExpectBegin() + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE migrations (id VARCHAR(255) PRIMARY KEY)`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT id FROM migrations`)). + WillReturnRows(sqlmock.NewRows([]string{"1"})) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "migrations" WHERE (id = $1)`)). + WithArgs("1"). + WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1)) + m.mock. + ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "migrations" WHERE (id = $1)`)). + WithArgs("2"). + WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0)) + m.mock. + ExpectExec(regexp.QuoteMeta(`ALTER TABLE "test_model" ALTER COLUMN "name" TYPE varchar(100)`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock. + ExpectExec(regexp.QuoteMeta(`INSERT INTO migrations (id) VALUES ($1)`)). + WithArgs("2"). + WillReturnResult(sqlmock.NewResult(1, 1)) + m.mock.ExpectCommit() + + err := m.Migrate.MigrateNextTo(m.Migration_TestModelFirst().ID) + + assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_MigratePreviousTo() { + m.RefreshMigrate() + m.Migrate.Add(m.Migration_TestModelFirst()) + m.Migrate.Add(m.Migration_TestModelSecond()) + + m.mock. + ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)). + WillReturnResult(sqlmock.NewResult(1, 1)) + + err := m.Migrate.MigratePreviousTo(m.Migration_TestModelSecond().ID) + + assert.Error(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) +} + +func (m *MigrateTest) Test_Close() { + m.RefreshMigrate() + m.mock.ExpectClose() + err := m.Migrate.Close() + + assert.NoError(m.T(), err) + assert.NoError(m.T(), m.mock.ExpectationsWereMet()) } func TestMigrate_Migrate(t *testing.T) { diff --git a/core/models_test.go b/core/models_test.go new file mode 100644 index 0000000..6c26666 --- /dev/null +++ b/core/models_test.go @@ -0,0 +1,11 @@ +package core + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestModels_TableName(t *testing.T) { + assert.NotEmpty(t, (User{}).TableName()) +} diff --git a/core/orm_test.go b/core/orm_test.go new file mode 100644 index 0000000..404c947 --- /dev/null +++ b/core/orm_test.go @@ -0,0 +1,73 @@ +package core + +import ( + "database/sql" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestORM_NewORM(t *testing.T) { + var ( + db *sql.DB + err error + ) + + defer func() { + require.Nil(t, recover()) + }() + + db, _, err = sqlmock.New() + require.NoError(t, err) + + config := DatabaseConfig{ + Connection: db, + Logging: true, + TablePrefix: "", + MaxOpenConnections: 10, + MaxIdleConnections: 10, + ConnectionLifetime: 100, + } + + _ = NewORM(config) +} + +func TestORM_createDB_Fail(t *testing.T) { + defer func() { + assert.NotNil(t, recover()) + }() + + NewORM(DatabaseConfig{Connection: nil}) +} + +func TestORM_CloseDB(t *testing.T) { + var ( + db *sql.DB + dbMock sqlmock.Sqlmock + err error + ) + + defer func() { + require.Nil(t, recover()) + }() + + db, dbMock, err = sqlmock.New() + require.NoError(t, err) + + config := DatabaseConfig{ + Connection: db, + Logging: true, + TablePrefix: "", + MaxOpenConnections: 10, + MaxIdleConnections: 10, + ConnectionLifetime: 100, + } + + dbMock.ExpectClose() + orm := NewORM(config) + orm.CloseDB() + + assert.NoError(t, dbMock.ExpectationsWereMet()) +} diff --git a/core/sentry.go b/core/sentry.go index 0d3d7a7..763ef73 100644 --- a/core/sentry.go +++ b/core/sentry.go @@ -287,7 +287,7 @@ func (t *SentryTaggedStruct) GetProperty(v interface{}, property string) (name s err = fmt.Errorf("cannot find property `%s`", property) } - field := val.FieldByName(property) + field := reflect.Indirect(val.FieldByName(property)) if !field.IsValid() { err = fmt.Errorf("invalid property, got %s", field.String()) return @@ -477,4 +477,4 @@ func getErrorCause(err error) error { return nil } return cer.Cause() -} \ No newline at end of file +} diff --git a/core/sentry_test.go b/core/sentry_test.go new file mode 100644 index 0000000..c263168 --- /dev/null +++ b/core/sentry_test.go @@ -0,0 +1,114 @@ +package core + +import ( + "errors" + "testing" + + "github.com/getsentry/raven-go" + pkgErrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type SampleStruct struct { + ID int + Pointer *int + Field string +} + +type SentryTest struct { + suite.Suite + sentry *Sentry + structTags *SentryTaggedStruct + scalarTags *SentryTaggedScalar +} + +func (s *SentryTest) SetupTest() { + s.structTags = NewTaggedStruct(SampleStruct{}, "struct", map[string]string{"fake": "prop"}) + s.scalarTags = NewTaggedScalar("", "scalar", "Scalar") + require.Equal(s.T(), "struct", s.structTags.GetContextKey()) + require.Equal(s.T(), "scalar", s.scalarTags.GetContextKey()) + require.Equal(s.T(), "", s.structTags.GetName()) + require.Equal(s.T(), "Scalar", s.scalarTags.GetName()) + s.structTags.Tags = map[string]string{} +} + +func (s *SentryTest) TestStruct_AddTag() { + s.structTags.AddTag("test field", "Field") + require.NotEmpty(s.T(), s.structTags.GetTags()) + + tags, err := s.structTags.BuildTags(SampleStruct{Field: "value"}) + require.NoError(s.T(), err) + require.NotEmpty(s.T(), tags) + + i, ok := tags["test field"] + require.True(s.T(), ok) + assert.Equal(s.T(), "value", i) +} + +func (s *SentryTest) TestStruct_GetProperty() { + s.structTags.AddTag("test field", "Field") + name, value, err := s.structTags.GetProperty(SampleStruct{Field: "test"}, "Field") + require.NoError(s.T(), err) + assert.Equal(s.T(), "test field", name) + assert.Equal(s.T(), "test", value) +} + +func (s *SentryTest) TestStruct_GetProperty_InvalidStruct() { + _, _, err := s.structTags.GetProperty(nil, "Field") + require.Error(s.T(), err) + assert.Equal(s.T(), "invalid value provided", err.Error()) +} + +func (s *SentryTest) TestStruct_GetProperty_GotScalar() { + _, _, err := s.structTags.GetProperty("str", "Field") + require.Error(s.T(), err) + assert.Equal(s.T(), "passed value must be struct, str provided", err.Error()) +} + +func (s *SentryTest) TestStruct_GetProperty_InvalidType() { + _, _, err := s.structTags.GetProperty(Sentry{}, "Field") + require.Error(s.T(), err) + assert.Equal(s.T(), "passed value should be of type `core.SampleStruct`, got `core.Sentry` instead", err.Error()) +} + +func (s *SentryTest) TestStruct_GetProperty_CannotFindProperty() { + _, _, err := s.structTags.GetProperty(SampleStruct{ID: 1}, "ID") + require.Error(s.T(), err) + assert.Equal(s.T(), "cannot find property `ID`", err.Error()) +} + +func (s *SentryTest) TestStruct_GetProperty_InvalidProperty() { + s.structTags.AddTag("test invalid", "Pointer") + _, _, err := s.structTags.GetProperty(SampleStruct{Pointer: nil}, "Pointer") + require.Error(s.T(), err) + assert.Equal(s.T(), "invalid property, got ", err.Error()) +} + +func TestSentry_newRavenStackTrace_Fail(t *testing.T) { + defer func() { + assert.NotNil(t, recover()) + }() + + newRavenStackTrace(nil, errors.New("error"), 0) +} + +func TestSentry_newRavenStackTrace(t *testing.T) { + st := newRavenStackTrace(&raven.Client{}, errors.New("error"), 0) + + require.NotNil(t, st) + assert.NotEmpty(t, st.Frames) +} + +func TestSentry_newRavenStackTrace_ErrorsPkg(t *testing.T) { + err := pkgErrors.New("error") + st := newRavenStackTrace(&raven.Client{}, err, 0) + + require.NotNil(t, st) + assert.NotEmpty(t, st.Frames) +} + +func TestSentry_Suite(t *testing.T) { + suite.Run(t, new(SentryTest)) +} diff --git a/core/template_test.go b/core/template_test.go new file mode 100644 index 0000000..05e6e9f --- /dev/null +++ b/core/template_test.go @@ -0,0 +1,56 @@ +package core + +import ( + "fmt" + "html/template" + "io/ioutil" + "os" + "path" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +var ( + testTemplatesDir = path.Join(os.TempDir(), "templates_test_dir") + testTemplatesFile = path.Join(testTemplatesDir, "tpl%d.html") +) + +type TemplateTest struct { + suite.Suite + renderer Renderer +} + +func (t *TemplateTest) SetupTest() { + if _, err := os.Stat(testTemplatesDir); err != nil && os.IsNotExist(err) { + err := os.Mkdir(testTemplatesDir, os.ModePerm) + require.Nil(t.T(), err) + data1 := []byte(`data {{template "body" .}}`) + data2 := []byte(`{{define "body"}}test {{"test" | trans}}{{end}}`) + err1 := ioutil.WriteFile(fmt.Sprintf(testTemplatesFile, 1), data1, os.ModePerm) + err2 := ioutil.WriteFile(fmt.Sprintf(testTemplatesFile, 2), data2, os.ModePerm) + require.Nil(t.T(), err1) + require.Nil(t.T(), err2) + } + + t.renderer = NewRenderer(template.FuncMap{ + "trans": func(data string) string { + if data == "test" { + return "ok" + } + + return "fail" + }, + }) +} + +func (t *TemplateTest) Test_Push() { + tpl := t.renderer.Push("index", fmt.Sprintf(testTemplatesFile, 1), fmt.Sprintf(testTemplatesFile, 2)) + assert.Equal(t.T(), 3, len(tpl.Templates())) +} + +func TestTemplate_Suite(t *testing.T) { + suite.Run(t, new(TemplateTest)) +}