|
|
|
package writefreely
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"encoding/gob"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
uuid "github.com/nu7hatch/gouuid"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var testDB *sql.DB
|
|
|
|
|
|
|
|
type ScopedTestBody func(*sql.DB)
|
|
|
|
|
|
|
|
// TestMain provides testing infrastructure within this package.
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
rand.Seed(time.Now().UTC().UnixNano())
|
|
|
|
gob.Register(&User{})
|
|
|
|
|
|
|
|
if runMySQLTests() {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
testDB, err = initMySQL(os.Getenv("WF_USER"), os.Getenv("WF_PASSWORD"), os.Getenv("WF_DB"), os.Getenv("WF_HOST"))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code := m.Run()
|
|
|
|
if runMySQLTests() {
|
|
|
|
if closeErr := testDB.Close(); closeErr != nil {
|
|
|
|
fmt.Println(closeErr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
os.Exit(code)
|
|
|
|
}
|
|
|
|
|
|
|
|
func runMySQLTests() bool {
|
|
|
|
return len(os.Getenv("TEST_MYSQL")) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func initMySQL(dbUser, dbPassword, dbName, dbHost string) (*sql.DB, error) {
|
|
|
|
if dbUser == "" || dbPassword == "" {
|
|
|
|
return nil, errors.New("database user or password not set")
|
|
|
|
}
|
|
|
|
if dbHost == "" {
|
|
|
|
dbHost = "localhost"
|
|
|
|
}
|
|
|
|
if dbName == "" {
|
|
|
|
dbName = "writefreely"
|
|
|
|
}
|
|
|
|
|
|
|
|
dsn := fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?charset=utf8mb4&parseTime=true", dbUser, dbPassword, dbHost, dbName)
|
|
|
|
db, err := sql.Open("mysql", dsn)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := ensureMySQL(db); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return db, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ensureMySQL(db *sql.DB) error {
|
|
|
|
if err := db.Ping(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
db.SetMaxOpenConns(250)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// withTestDB provides a scoped database connection.
|
|
|
|
func withTestDB(t *testing.T, testBody ScopedTestBody) {
|
|
|
|
db, cleanup, err := newTestDatabase(testDB,
|
|
|
|
os.Getenv("WF_USER"),
|
|
|
|
os.Getenv("WF_PASSWORD"),
|
|
|
|
os.Getenv("WF_DB"),
|
|
|
|
os.Getenv("WF_HOST"),
|
|
|
|
)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
defer func() {
|
|
|
|
assert.NoError(t, cleanup())
|
|
|
|
}()
|
|
|
|
|
|
|
|
testBody(db)
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTestDatabase creates a new temporary test database. When a test
|
|
|
|
// database connection is returned, it will have created a new database and
|
|
|
|
// initialized it with tables from a reference database.
|
|
|
|
func newTestDatabase(base *sql.DB, dbUser, dbPassword, dbName, dbHost string) (*sql.DB, func() error, error) {
|
|
|
|
var err error
|
|
|
|
var baseName = dbName
|
|
|
|
|
|
|
|
if baseName == "" {
|
|
|
|
row := base.QueryRow("SELECT DATABASE()")
|
|
|
|
err := row.Scan(&baseName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tUUID, _ := uuid.NewV4()
|
|
|
|
suffix := strings.Replace(tUUID.String(), "-", "_", -1)
|
|
|
|
newDBName := baseName + suffix
|
|
|
|
_, err = base.Exec("CREATE DATABASE " + newDBName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
newDB, err := initMySQL(dbUser, dbPassword, newDBName, dbHost)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rows, err := base.Query("SHOW TABLES IN " + baseName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
for rows.Next() {
|
|
|
|
var tableName string
|
|
|
|
if err := rows.Scan(&tableName); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
query := fmt.Sprintf("CREATE TABLE %s LIKE %s.%s", tableName, baseName, tableName)
|
|
|
|
if _, err := newDB.Exec(query); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup := func() error {
|
|
|
|
if closeErr := newDB.Close(); closeErr != nil {
|
|
|
|
fmt.Println(closeErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = base.Exec("DROP DATABASE " + newDBName)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return newDB, cleanup, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func countRows(t *testing.T, ctx context.Context, db *sql.DB, count int, query string, args ...interface{}) {
|
|
|
|
var returned int
|
|
|
|
err := db.QueryRowContext(ctx, query, args...).Scan(&returned)
|
|
|
|
assert.NoError(t, err, "error executing query %s and args %s", query, args)
|
|
|
|
assert.Equal(t, count, returned, "unexpected return count %d, expected %d from %s and args %s", returned, count, query, args)
|
|
|
|
}
|