mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-28 12:46:09 +01:00
f550e356d6
Running `make test-backend` will delete `data/` due to reloading the configuration and resetting the appdatapath. This PR removes this unnecessary config reload but also adds extra code in to the unittest main to prevent its cleanup from deleting the wrong directory. Signed-off-by: Andrew Thornton <art27@cantab.net>
348 lines
9.3 KiB
Go
348 lines
9.3 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package migrations
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
"code.gitea.io/gitea/models/unittest"
|
|
"code.gitea.io/gitea/modules/base"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/unknwon/com"
|
|
"xorm.io/xorm"
|
|
"xorm.io/xorm/names"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
giteaRoot := base.SetupGiteaRoot()
|
|
if giteaRoot == "" {
|
|
fmt.Println("Environment variable $GITEA_ROOT not set")
|
|
os.Exit(1)
|
|
}
|
|
giteaBinary := "gitea"
|
|
if runtime.GOOS == "windows" {
|
|
giteaBinary += ".exe"
|
|
}
|
|
setting.AppPath = path.Join(giteaRoot, giteaBinary)
|
|
if _, err := os.Stat(setting.AppPath); err != nil {
|
|
fmt.Printf("Could not find gitea binary at %s\n", setting.AppPath)
|
|
os.Exit(1)
|
|
}
|
|
|
|
giteaConf := os.Getenv("GITEA_CONF")
|
|
if giteaConf == "" {
|
|
giteaConf = path.Join(filepath.Dir(setting.AppPath), "integrations/sqlite.ini")
|
|
fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
|
|
}
|
|
|
|
if !path.IsAbs(giteaConf) {
|
|
setting.CustomConf = path.Join(giteaRoot, giteaConf)
|
|
} else {
|
|
setting.CustomConf = giteaConf
|
|
}
|
|
|
|
tmpDataPath, err := ioutil.TempDir("", "data")
|
|
if err != nil {
|
|
fmt.Printf("Unable to create temporary data path %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
setting.AppDataPath = tmpDataPath
|
|
|
|
setting.SetCustomPathAndConf("", "", "")
|
|
setting.LoadForTest()
|
|
git.CheckLFSVersion()
|
|
setting.InitDBConfig()
|
|
setting.NewLogServices(true)
|
|
|
|
exitStatus := m.Run()
|
|
|
|
if err := removeAllWithRetry(setting.RepoRootPath); err != nil {
|
|
fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
|
|
}
|
|
if err := removeAllWithRetry(tmpDataPath); err != nil {
|
|
fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err)
|
|
}
|
|
os.Exit(exitStatus)
|
|
}
|
|
|
|
func removeAllWithRetry(dir string) error {
|
|
var err error
|
|
for i := 0; i < 20; i++ {
|
|
err = os.RemoveAll(dir)
|
|
if err == nil {
|
|
break
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func newXORMEngine() (*xorm.Engine, error) {
|
|
if err := db.InitEngine(context.Background()); err != nil {
|
|
return nil, err
|
|
}
|
|
x := unittest.GetXORMEngine()
|
|
return x, nil
|
|
}
|
|
|
|
func deleteDB() error {
|
|
switch {
|
|
case setting.Database.UseSQLite3:
|
|
if err := util.Remove(setting.Database.Path); err != nil {
|
|
return err
|
|
}
|
|
return os.MkdirAll(path.Dir(setting.Database.Path), os.ModePerm)
|
|
|
|
case setting.Database.UseMySQL:
|
|
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/",
|
|
setting.Database.User, setting.Database.Passwd, setting.Database.Host))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", setting.Database.Name)); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
case setting.Database.UsePostgreSQL:
|
|
db, err := sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/?sslmode=%s",
|
|
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.SSLMode))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", setting.Database.Name)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
|
|
return err
|
|
}
|
|
db.Close()
|
|
|
|
// Check if we need to setup a specific schema
|
|
if len(setting.Database.Schema) != 0 {
|
|
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
|
|
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer schrows.Close()
|
|
|
|
if !schrows.Next() {
|
|
// Create and setup a DB schema
|
|
_, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Make the user's default search path the created schema; this will affect new connections
|
|
_, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
case setting.Database.UseMSSQL:
|
|
host, port := setting.ParseMSSQLHostPort(setting.Database.Host)
|
|
db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
|
|
host, port, "master", setting.Database.User, setting.Database.Passwd))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer db.Close()
|
|
|
|
if _, err = db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS [%s]", setting.Database.Name)); err != nil {
|
|
return err
|
|
}
|
|
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE [%s]", setting.Database.Name)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// prepareTestEnv prepares the test environment and reset the database. The skip parameter should usually be 0.
|
|
// Provide models to be sync'd with the database - in particular any models you expect fixtures to be loaded from.
|
|
//
|
|
// fixtures in `models/migrations/fixtures/<TestName>` will be loaded automatically
|
|
func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.Engine, func()) {
|
|
t.Helper()
|
|
ourSkip := 2
|
|
ourSkip += skip
|
|
deferFn := PrintCurrentTest(t, ourSkip)
|
|
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
|
|
|
|
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
|
|
setting.RepoRootPath))
|
|
|
|
if err := deleteDB(); err != nil {
|
|
t.Errorf("unable to reset database: %v", err)
|
|
return nil, deferFn
|
|
}
|
|
|
|
x, err := newXORMEngine()
|
|
assert.NoError(t, err)
|
|
if x != nil {
|
|
oldDefer := deferFn
|
|
deferFn = func() {
|
|
oldDefer()
|
|
if err := x.Close(); err != nil {
|
|
t.Errorf("error during close: %v", err)
|
|
}
|
|
if err := deleteDB(); err != nil {
|
|
t.Errorf("unable to reset database: %v", err)
|
|
}
|
|
}
|
|
}
|
|
if err != nil {
|
|
return x, deferFn
|
|
}
|
|
|
|
if len(syncModels) > 0 {
|
|
if err := x.Sync2(syncModels...); err != nil {
|
|
t.Errorf("error during sync: %v", err)
|
|
return x, deferFn
|
|
}
|
|
}
|
|
|
|
fixturesDir := filepath.Join(filepath.Dir(setting.AppPath), "models", "migrations", "fixtures", t.Name())
|
|
|
|
if _, err := os.Stat(fixturesDir); err == nil {
|
|
t.Logf("initializing fixtures from: %s", fixturesDir)
|
|
if err := unittest.InitFixtures(
|
|
unittest.FixturesOptions{
|
|
Dir: fixturesDir,
|
|
}, x); err != nil {
|
|
t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
|
|
return x, deferFn
|
|
}
|
|
if err := unittest.LoadFixtures(x); err != nil {
|
|
t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err)
|
|
return x, deferFn
|
|
}
|
|
} else if !os.IsNotExist(err) {
|
|
t.Errorf("unexpected error whilst checking for existence of fixtures: %v", err)
|
|
} else {
|
|
t.Logf("no fixtures found in: %s", fixturesDir)
|
|
}
|
|
|
|
return x, deferFn
|
|
}
|
|
|
|
func Test_dropTableColumns(t *testing.T) {
|
|
x, deferable := prepareTestEnv(t, 0)
|
|
if x == nil || t.Failed() {
|
|
defer deferable()
|
|
return
|
|
}
|
|
defer deferable()
|
|
|
|
type DropTest struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
FirstColumn string
|
|
ToDropColumn string `xorm:"unique"`
|
|
AnotherColumn int64
|
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
|
}
|
|
|
|
columns := []string{
|
|
"first_column",
|
|
"to_drop_column",
|
|
"another_column",
|
|
"created_unix",
|
|
"updated_unix",
|
|
}
|
|
|
|
for i := range columns {
|
|
x.SetMapper(names.GonicMapper{})
|
|
if err := x.Sync2(new(DropTest)); err != nil {
|
|
t.Errorf("unable to create DropTest table: %v", err)
|
|
return
|
|
}
|
|
sess := x.NewSession()
|
|
if err := sess.Begin(); err != nil {
|
|
sess.Close()
|
|
t.Errorf("unable to begin transaction: %v", err)
|
|
return
|
|
}
|
|
if err := dropTableColumns(sess, "drop_test", columns[i:]...); err != nil {
|
|
sess.Close()
|
|
t.Errorf("Unable to drop columns[%d:]: %s from drop_test: %v", i, columns[i:], err)
|
|
return
|
|
}
|
|
if err := sess.Commit(); err != nil {
|
|
sess.Close()
|
|
t.Errorf("unable to commit transaction: %v", err)
|
|
return
|
|
}
|
|
sess.Close()
|
|
if err := x.DropTables(new(DropTest)); err != nil {
|
|
t.Errorf("unable to drop table: %v", err)
|
|
return
|
|
}
|
|
for j := range columns[i+1:] {
|
|
x.SetMapper(names.GonicMapper{})
|
|
if err := x.Sync2(new(DropTest)); err != nil {
|
|
t.Errorf("unable to create DropTest table: %v", err)
|
|
return
|
|
}
|
|
dropcols := append([]string{columns[i]}, columns[j+i+1:]...)
|
|
sess := x.NewSession()
|
|
if err := sess.Begin(); err != nil {
|
|
sess.Close()
|
|
t.Errorf("unable to begin transaction: %v", err)
|
|
return
|
|
}
|
|
if err := dropTableColumns(sess, "drop_test", dropcols...); err != nil {
|
|
sess.Close()
|
|
t.Errorf("Unable to drop columns: %s from drop_test: %v", dropcols, err)
|
|
return
|
|
}
|
|
if err := sess.Commit(); err != nil {
|
|
sess.Close()
|
|
t.Errorf("unable to commit transaction: %v", err)
|
|
return
|
|
}
|
|
sess.Close()
|
|
if err := x.DropTables(new(DropTest)); err != nil {
|
|
t.Errorf("unable to drop table: %v", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|