<commit_before>package integration

import (
	"fmt"
	"strings"

	"github.com/Nivl/go-rest-tools/dependencies"
	db "github.com/Nivl/go-sqldb"
	sqlx "github.com/Nivl/go-sqldb/implementations/sqlxdb"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
)

// Wrapper is an helper to simplify the encapsulation of integration
// tests
type Wrapper struct {
	Deps      dependencies.Dependencies
	masterDB  db.Connection
	tmpDBName string
}

// New creates a new Wrapper
func New(deps dependencies.Dependencies) (*Wrapper, error) {
	it := &Wrapper{
		Deps: deps,

		// masterDB contains a DB connection to the default database.
		// this is needed because postgres won't allow us to drop the
		// current database, so we use the default one to drop the
		// temporary one
		masterDB: deps.DB(),

		// for the new table name we use a uuid without "-"
		tmpDBName: strings.Replace(uuid.NewV4().String(), "-", "", -1),
	}

	// We get the current database name
	masterDbName := ""
	if err := it.masterDB.Get(&masterDbName, "SELECT current_database();"); err != nil {
		return nil, errors.Wrap(err, "failed getting master database name")
	}

	// We create a new Database to avoid races between tests
	stmt := fmt.Sprintf(`CREATE DATABASE "%s" TEMPLATE "%s";`, it.tmpDBName, masterDbName)
	if _, err := it.masterDB.Exec(stmt); err != nil {
		return nil, errors.Wrap(err, "failed creating tmp database")
	}

	// We set this new database as the new current one, to do that we
	// get the DSN of the current connection and swap the table name by
	// the new one
	masterDBString := fmt.Sprintf("dbname=%s", masterDbName)
	tmpDBString := fmt.Sprintf("dbname=%s", it.tmpDBName)
	tmpDBDSN := strings.Replace(it.masterDB.DSN(), masterDBString, tmpDBString, -1)
	tmpDB, err := sqlx.New(tmpDBDSN)
	if err != nil {
		return nil, errors.Wrap(err, "could not connect to the tmp table")
	}
	it.Deps.SetDB(tmpDB)

	return it, nil
}

// Close cleans up the tests by deleting the database
func (it *Wrapper) Close() error {
	if err := it.Deps.DB().Close(); err != nil {
		return err
	}
	stmt := fmt.Sprintf(`DROP DATABASE "%s";`, it.tmpDBName)
	if _, err := it.masterDB.Exec(stmt); err != nil {
		return err
	}
	return it.masterDB.Close()
}

// RecoverPanic prevents a panic from not calling the defer in the othr goroutines
func (it *Wrapper) RecoverPanic() {<commit_msg>fix(testing): Fix concurency issue on integration tests<commit_after>package integration

import (
	"fmt"
	"strings"
	"sync"

	"github.com/Nivl/go-rest-tools/dependencies"
	db "github.com/Nivl/go-sqldb"
	sqlx "github.com/Nivl/go-sqldb/implementations/sqlxdb"
	"github.com/pkg/errors"
	"github.com/pressly/goose"
	uuid "github.com/satori/go.uuid"
)

var (
	// dbLock is used to prevent concurent create/update on the temporary table
	dbLock sync.Mutex

	// templateDBCreated is used to check if the template database has already
	// been created
	templateDBCreated = false

	// masterDbName holds the name of the default database
	masterDbName = ""
)

func createTemplateDatabase(con db.Connection, templateName, migrationFolder string) error {
	if templateDBCreated {
		return nil
	}

	// We get the current database name
	if err := con.Get(&masterDbName, "SELECT current_database();"); err != nil {
		return fmt.Errorf("failed getting master database name: %s", err.Error())
	}

	// We drop whatever exists
	stmt := fmt.Sprintf(`DROP DATABASE IF EXISTS "%s";`, templateName)
	if _, err := con.Exec(stmt); err != nil {
		return err
	}

	// We create the template
	stmt = fmt.Sprintf(`CREATE DATABASE "%s";`, templateName)
	if _, err := con.Exec(stmt); err != nil {
		return err
	}

	// We now need to connect to this database to create all the table. We
	// get the DSN of the current connection and swap the table name by
	// the new one
	masterDBString := fmt.Sprintf("dbname=%s", masterDbName)
	tplDBString := fmt.Sprintf("dbname=%s", templateName)
	tplDBDSN := strings.Replace(con.DSN(), masterDBString, tplDBString, -1)
	tplDB, err := sqlx.New(tplDBDSN)
	if err != nil {
		return fmt.Errorf("could not connect to the tmp table: %s", err.Error())
	}
	defer tplDB.Close()

	// We apply the migration to the newly created database
	return goose.Up(tplDB.SQL(), migrationFolder)
}

// New creates a new Wrapper
func New(deps dependencies.Dependencies, migrationFolder string) (*Wrapper, error) {
	dbLock.Lock()

	templateName := "test_template"
	if err := createTemplateDatabase(deps.DB(), templateName, migrationFolder); err != nil {
		dbLock.Unlock()
		return nil, err
	}
	dbLock.Unlock()

	it := &Wrapper{
		Deps: deps,

		// masterDB contains a DB connection to the default database.
		// this is needed because postgres won't allow us to drop the
		// current database, so we use the default one to drop the
		// temporary one
		masterDB: deps.DB(),

		// for the new table name we use a uuid without "-"
		tmpDBName: strings.Replace(uuid.NewV4().String(), "-", "", -1),
	}

	// We create a new Database to avoid races between tests
	stmt := fmt.Sprintf(`CREATE DATABASE "%s" TEMPLATE "%s";`, it.tmpDBName, templateName)
	if _, err := it.masterDB.Exec(stmt); err != nil {
		return nil, errors.Wrap(err, "failed creating tmp database")
	}

	// We set this new database as the new current one, to do that we
	// get the DSN of the current connection and swap the table name by
	// the new one
	masterDBString := fmt.Sprintf("dbname=%s", masterDbName)
	tmpDBString := fmt.Sprintf("dbname=%s", it.tmpDBName)
	tmpDBDSN := strings.Replace(it.masterDB.DSN(), masterDBString, tmpDBString, -1)
	tmpDB, err := sqlx.New(tmpDBDSN)
	if err != nil {
		return nil, errors.Wrap(err, "could not connect to the tmp table")
	}
	it.Deps.SetDB(tmpDB)

	return it, nil
}

// Wrapper is an helper to simplify the encapsulation of integration
// tests
type Wrapper struct {
	Deps      dependencies.Dependencies
	masterDB  db.Connection
	tmpDBName string
}

// Close cleans up the tests by deleting the database
func (it *Wrapper) Close() error {
	if err := it.Deps.DB().Close(); err != nil {
		return err
	}
	stmt := fmt.Sprintf(`DROP DATABASE "%s";`, it.tmpDBName)
	if _, err := it.masterDB.Exec(stmt); err != nil {
		return err
	}
	return it.masterDB.Close()
}

// RecoverPanic prevents a panic from not calling the defer in the othr goroutines
func (it *Wrapper) RecoverPanic() {
{'fraction_non_alphanumeric': 0.12450498970378585, 'fraction_numerical': 0.0011088230635197213, 'mean_word_length': 3.8457405986185726, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3085472', 'n_tokens_mistral': 2136, 'n_tokens_neox': 2067, 'n_words': 698}