/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package plugin_test import ( "context" "database/sql" "fmt" "os" "path/filepath" "testing" "time" "github.com/apache/answer/internal/base/data" "github.com/apache/answer/internal/migrations" "github.com/ory/dockertest/v3" "github.com/ory/dockertest/v3/docker" "github.com/segmentfault/pacman/cache" "github.com/segmentfault/pacman/log" "xorm.io/xorm" "xorm.io/xorm/schemas" ) var ( mysqlDBSetting = TestDBSetting{ Driver: string(schemas.MYSQL), ImageName: "mariadb", ImageVersion: "10.4.7", ENV: []string{"MYSQL_ROOT_PASSWORD=root", "MYSQL_DATABASE=answer", "MYSQL_ROOT_HOST=%"}, PortID: "3306/tcp", Connection: "root:root@(localhost:%s)/answer?parseTime=true", // port is not fixed, it will be got by port id } postgresDBSetting = TestDBSetting{ Driver: string(schemas.POSTGRES), ImageName: "postgres", ImageVersion: "14", ENV: []string{"POSTGRES_USER=root", "POSTGRES_PASSWORD=root", "POSTGRES_DB=answer", "LISTEN_ADDRESSES='*'"}, PortID: "5432/tcp", Connection: "host=localhost port=%s user=root password=root dbname=answer sslmode=disable", } sqlite3DBSetting = TestDBSetting{ Driver: string(schemas.SQLITE), Connection: filepath.Join(os.TempDir(), "answer-test-data.db"), } dbSettingMapping = map[string]TestDBSetting{ mysqlDBSetting.Driver: mysqlDBSetting, sqlite3DBSetting.Driver: sqlite3DBSetting, postgresDBSetting.Driver: postgresDBSetting, } // after all test down will execute tearDown function to clean-up tearDown func() // testDataSource used for repo testing testDataSource *data.Data testCache cache.Cache ) func TestMain(t *testing.M) { dbSetting, ok := dbSettingMapping[os.Getenv("TEST_DB_DRIVER")] if !ok { // Use sqlite3 to test. dbSetting = dbSettingMapping[string(schemas.SQLITE)] } if dbSetting.Driver == string(schemas.SQLITE) { _ = os.RemoveAll(dbSetting.Connection) } if err := initTestDataSource(dbSetting); err != nil { panic(err) } log.Info("init test database successfully") ret := t.Run() if tearDown != nil { tearDown() } os.Exit(ret) } type TestDBSetting struct { Driver string ImageName string ImageVersion string ENV []string PortID string Connection string } func initTestDataSource(dbSetting TestDBSetting) error { connection, imageCleanUp, err := initDatabaseImage(dbSetting) if err != nil { return err } dbSetting.Connection = connection dbEngine, err := initDatabase(dbSetting) if err != nil { return err } newCache, err := initCache() if err != nil { return err } newData, dbCleanUp, err := data.NewData(dbEngine, newCache) if err != nil { return err } testDataSource = newData testCache = newCache tearDown = func() { dbCleanUp() log.Info("cleanup test database successfully") imageCleanUp() log.Info("cleanup test database image successfully") } return nil } func initDatabaseImage(dbSetting TestDBSetting) (connection string, cleanup func(), err error) { // sqlite3 don't need to set up image if dbSetting.Driver == string(schemas.SQLITE) { return dbSetting.Connection, func() { log.Info("remove database", dbSetting.Connection) err = os.Remove(dbSetting.Connection) if err != nil { log.Error(err) } }, nil } pool, err := dockertest.NewPool("") pool.MaxWait = time.Minute * 5 if err != nil { return "", nil, fmt.Errorf("could not connect to docker: %s", err) } // resource, err := pool.Run(dbSetting.ImageName, dbSetting.ImageVersion, dbSetting.ENV) resource, err := pool.RunWithOptions(&dockertest.RunOptions{ Repository: dbSetting.ImageName, Tag: dbSetting.ImageVersion, Env: dbSetting.ENV, }, func(config *docker.HostConfig) { config.AutoRemove = true config.RestartPolicy = docker.RestartPolicy{Name: "no"} }) if err != nil { return "", nil, fmt.Errorf("could not pull resource: %s", err) } connection = fmt.Sprintf(dbSetting.Connection, resource.GetPort(dbSetting.PortID)) if err := pool.Retry(func() error { db, err := sql.Open(dbSetting.Driver, connection) if err != nil { return err } return db.Ping() }); err != nil { return "", nil, fmt.Errorf("could not connect to database: %s", err) } return connection, func() { _ = pool.Purge(resource) }, nil } func initDatabase(dbSetting TestDBSetting) (dbEngine *xorm.Engine, err error) { dataConf := &data.Database{Driver: dbSetting.Driver, Connection: dbSetting.Connection} dbEngine, err = data.NewDB(true, dataConf) if err != nil { return nil, fmt.Errorf("connection to database failed: %s", err) } if err := migrations.NewMentor(context.TODO(), dbEngine, &migrations.InitNeedUserInputData{ Language: "en_US", SiteName: "ANSWER", SiteURL: "http://127.0.0.1:8080/", ContactEmail: "answer@answer.com", AdminName: "admin", AdminPassword: "admin", AdminEmail: "answer@answer.com", }).InitDB(); err != nil { return nil, fmt.Errorf("migrations init database failed: %s", err) } return dbEngine, nil } func initCache() (newCache cache.Cache, err error) { newCache, _, err = data.NewCache(&data.CacheConf{}) return newCache, err }