150 lines
3.4 KiB
Go
150 lines
3.4 KiB
Go
package test
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
"os"
|
||
"pink_fox/inner/config"
|
||
"pink_fox/packages/fw"
|
||
"regexp"
|
||
"strings"
|
||
)
|
||
|
||
var (
|
||
dbTest *DBTest = nil
|
||
)
|
||
|
||
type DBTest struct {
|
||
db *sql.DB
|
||
seedersPath string
|
||
}
|
||
|
||
func NewDBTest() *DBTest {
|
||
if dbTest == nil {
|
||
path := "/app/config-test.yml"
|
||
dbConf, err := config.LoadTestConfig(path)
|
||
if err != nil {
|
||
panic(err.Add("ошибка загрузки конфига"))
|
||
}
|
||
|
||
db, err := fw.CreateConnection(dbConf.Host, dbConf.Port, dbConf.User, dbConf.Password, dbConf.Database)
|
||
if err != nil {
|
||
panic(err.Add("ошибка соединение с базой данной"))
|
||
}
|
||
|
||
err = refreshDatabase(db, dbConf.Migrations, dbConf.Database, true)
|
||
if err != nil {
|
||
panic(err.Add("ошибка обновление базы данных"))
|
||
}
|
||
|
||
dbTest = &DBTest{
|
||
db: db,
|
||
seedersPath: "/app/database/seeders",
|
||
}
|
||
}
|
||
return dbTest
|
||
}
|
||
|
||
func (it *DBTest) GetDB() *sql.DB {
|
||
return it.db
|
||
}
|
||
|
||
func (it *DBTest) Seed(filename string) *DBTest {
|
||
path := it.seedersPath + "/" + filename
|
||
queryByte, err := os.ReadFile(path)
|
||
if err != nil {
|
||
panic(fw.ErrStr(fmt.Sprintf("ошибка загрузки файла для %s: %s", path, err)))
|
||
}
|
||
|
||
tx, err := it.db.Begin()
|
||
if err != nil {
|
||
panic(fw.ErrStr(fmt.Sprintf("ошибка запуска транзакции: %s", err)))
|
||
}
|
||
defer func() {
|
||
_ = tx.Rollback()
|
||
}()
|
||
|
||
cleanedData := it.removeSQLComments(string(queryByte))
|
||
queries := it.splitSQLQueries(cleanedData)
|
||
for _, query := range queries {
|
||
query = strings.TrimSpace(query)
|
||
if query == "" {
|
||
continue
|
||
}
|
||
|
||
if _, err = tx.Exec(query); err != nil {
|
||
panic(fmt.Errorf("ошибка в запросе `%s`: %s", query, err))
|
||
}
|
||
}
|
||
|
||
err = tx.Commit()
|
||
if err != nil {
|
||
panic(fmt.Sprintf("ошибка подтверждения транзакции: %s", err))
|
||
}
|
||
|
||
return it
|
||
}
|
||
|
||
func refreshDatabase(db *sql.DB, migrationsPath, dbName string, disableLogger bool) fw.Error {
|
||
err := fw.DropAppTable(db, dbName)
|
||
if err != nil {
|
||
return err.Tap()
|
||
}
|
||
|
||
return fw.MigrateUp(db, migrationsPath, disableLogger)
|
||
}
|
||
|
||
func (it *DBTest) removeSQLComments(sql string) string {
|
||
// Удаляем однострочные комментарии (-- до конца строки)
|
||
reSingleLine := regexp.MustCompile(`--.*`)
|
||
sql = reSingleLine.ReplaceAllString(sql, "")
|
||
|
||
// Удаляем многострочные комментарии (/* ... */)
|
||
reMultiLine := regexp.MustCompile(`/\*.*?\*/`)
|
||
sql = reMultiLine.ReplaceAllString(sql, "")
|
||
|
||
return sql
|
||
}
|
||
|
||
// splitSQLQueries разбивает SQL-код на отдельные запросы
|
||
func (it *DBTest) splitSQLQueries(sql string) []string {
|
||
var queries []string
|
||
var buffer strings.Builder
|
||
inString := false
|
||
quoteChar := byte(0)
|
||
|
||
for i := 0; i < len(sql); i++ {
|
||
char := sql[i]
|
||
|
||
switch {
|
||
case char == '\'' || char == '"':
|
||
if !inString {
|
||
inString = true
|
||
quoteChar = char
|
||
} else if char == quoteChar {
|
||
// Проверяем, не экранирована ли кавычка
|
||
if i > 0 && sql[i-1] == '\\' {
|
||
continue
|
||
}
|
||
inString = false
|
||
quoteChar = 0
|
||
}
|
||
buffer.WriteByte(char)
|
||
|
||
case char == ';' && !inString:
|
||
queries = append(queries, buffer.String())
|
||
buffer.Reset()
|
||
|
||
default:
|
||
buffer.WriteByte(char)
|
||
}
|
||
}
|
||
|
||
// Добавляем последний запрос (если есть)
|
||
if buffer.Len() > 0 {
|
||
queries = append(queries, buffer.String())
|
||
}
|
||
|
||
return queries
|
||
}
|