2022-10-26 17:28:42 +03:00
package db_test
import (
2022-11-02 17:55:53 +03:00
"log"
2022-10-26 17:43:11 +03:00
"os"
2022-10-26 17:28:42 +03:00
"testing"
"git.bit5.ru/backend/db"
"git.bit5.ru/backend/errors"
"github.com/stretchr/testify/assert"
2022-11-02 17:55:53 +03:00
"github.com/stretchr/testify/require"
"github.com/go-logr/stdr"
2022-10-26 17:28:42 +03:00
)
2022-11-03 14:46:25 +03:00
const verbosity = 0
2022-11-02 17:55:53 +03:00
var logger = stdr . New ( log . New ( os . Stdout , "" , log . Lshortfile ) )
2022-10-26 17:43:11 +03:00
2022-10-28 10:52:25 +03:00
func getSettings ( ) db . Settings {
//TODO: use ENV settings as well
2022-11-03 14:46:25 +03:00
stdr . SetVerbosity ( verbosity )
2022-11-03 12:53:08 +03:00
return db . Settings { Host : "127.0.0.1" , Port : "3306" , User : "root" , Pass : "test" , Name : "tests" , Prefix : "tests" }
2022-10-28 10:52:25 +03:00
}
func getPool ( ) * db . Pool {
return db . OpenPool ( getSettings ( ) )
}
func getDBC ( p * db . Pool ) * db . DBC {
dbc := db . GetDBC ( p , logger )
2022-10-26 17:28:42 +03:00
return dbc
}
func TestDefaultClientCharsetAndCollation ( t * testing . T ) {
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
2022-10-26 17:28:42 +03:00
var result = make ( map [ string ] string )
2022-11-02 17:55:53 +03:00
characterSets , err := dbc . DB ( ) . Query ( "SHOW VARIABLES WHERE Variable_name in ('character_set_client', 'character_set_connection', 'character_set_results');" )
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
for characterSets . Next ( ) {
var variableName string
var value string
characterSets . Scan ( & variableName , & value )
result [ variableName ] = value
}
collations , err := dbc . DB ( ) . Query ( "show variables where Variable_name = 'collation_connection';" )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
for collations . Next ( ) {
var variableName string
var value string
collations . Scan ( & variableName , & value )
result [ variableName ] = value
}
assert . Equal ( t , "utf8" , result [ "character_set_client" ] )
assert . Equal ( t , "utf8" , result [ "character_set_connection" ] )
assert . Equal ( t , "utf8" , result [ "character_set_results" ] )
assert . Equal ( t , "utf8_general_ci" , result [ "collation_connection" ] )
}
func TestClientCharsetAndCollation ( t * testing . T ) {
2022-10-28 10:52:25 +03:00
DSNWithLatinCollation := getSettings ( )
2022-10-26 17:28:42 +03:00
DSNWithLatinCollation . Params = "?collation=latin1_swedish_ci"
2022-10-28 10:52:25 +03:00
pool := db . OpenPool ( DSNWithLatinCollation )
defer pool . Close ( )
2022-10-26 17:28:42 +03:00
var resultsLatin1 = make ( map [ string ] string )
2022-10-28 10:52:25 +03:00
dbLatin1 := db . GetDBC ( pool , logger )
2022-10-26 17:28:42 +03:00
characterSets , err := dbLatin1 . DB ( ) . Query ( "show variables where Variable_name in ('character_set_client', 'character_set_connection', 'character_set_results');" )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
for characterSets . Next ( ) {
var variableName string
var value string
characterSets . Scan ( & variableName , & value )
resultsLatin1 [ variableName ] = value
}
collations , err := dbLatin1 . DB ( ) . Query ( "show variables where Variable_name = 'collation_connection';" )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
for collations . Next ( ) {
var variableName string
var value string
collations . Scan ( & variableName , & value )
resultsLatin1 [ variableName ] = value
}
assert . Equal ( t , "latin1" , resultsLatin1 [ "character_set_client" ] )
assert . Equal ( t , "latin1" , resultsLatin1 [ "character_set_connection" ] )
assert . Equal ( t , "latin1" , resultsLatin1 [ "character_set_results" ] )
assert . Equal ( t , "latin1_swedish_ci" , resultsLatin1 [ "collation_connection" ] )
}
func createFooTable ( t * testing . T ) {
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
_ , err := dbc . DB ( ) . Exec ( "DROP TABLE IF EXISTS foo" )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
_ , err = dbc . DB ( ) . Exec ( "CREATE TABLE IF NOT EXISTS foo(id int not null)" )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
}
func TestTransactionCommit ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc1 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc1 ) )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
dbc . Commit ( )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 2 , countFoos ( t , dbc2 ) )
}
func TestTransactionCommitNestedAllOk ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
//begin 1
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc1 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc1 ) )
//begin 2
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(3)" ) . Exec ( )
//commit 2
dbc . Commit ( )
assert . EqualValues ( t , 3 , countFoos ( t , dbc ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc2 ) )
//commit 1
dbc . Commit ( )
assert . EqualValues ( t , 3 , countFoos ( t , dbc ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
db3 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 3 , countFoos ( t , db3 ) )
}
func TestTransactionCommitNestedRollback ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
//begin 1
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc1 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc1 ) )
//begin 2
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(3)" ) . Exec ( )
//rollback 2
dbc . Rollback ( )
//rollback above doesn't have an effect since we are in the
//nested transaction
assert . EqualValues ( t , 3 , countFoos ( t , dbc ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc2 ) )
//rollback 1
dbc . Rollback ( )
assert . EqualValues ( t , 0 , countFoos ( t , dbc ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
db3 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , db3 ) )
}
func TestTransactionRollbackOnDeferAllOK ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
fn := func ( ) {
dbc . Begin ( )
defer dbc . RollbackOnDefer ( )
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
fnNested := func ( ) {
dbc . Begin ( )
defer dbc . RollbackOnDefer ( )
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(3)" ) . Exec ( )
assert . EqualValues ( t , 3 , countFoos ( t , dbc ) )
dbc . Commit ( )
}
fnNested ( )
dbc . Commit ( )
}
fn ( )
2022-10-28 10:52:25 +03:00
dbc1 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 3 , countFoos ( t , dbc1 ) )
}
func TestTransactionRollbackOnDefer ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
fn := func ( ) {
dbc . Begin ( )
defer dbc . RollbackOnDefer ( )
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
fnNested := func ( ) {
dbc . Begin ( )
defer dbc . RollbackOnDefer ( )
dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(3)" ) . Exec ( )
assert . EqualValues ( t , 3 , countFoos ( t , dbc ) )
//commit is missing for some reason, emulating error
//dbc.Commit()
}
fnNested ( )
//commit is missing for some reason, emulating error
//dbc.Commit()
}
fn ( )
2022-10-28 10:52:25 +03:00
dbc1 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc1 ) )
}
func TestTransaction ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Transaction ( func ( dbs * db . DBC ) error {
2022-10-26 17:28:42 +03:00
dbs . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc1 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc1 ) )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
return nil
} ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 2 , countFoos ( t , dbc2 ) )
}
func TestTransactionRollbackOnError ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
err := dbc . Transaction ( func ( dbs * db . DBC ) error {
dbs . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
return errors . New ( "Opps" )
} )
assert . EqualValues ( t , "Opps" , err . Error ( ) )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc2 ) )
}
func TestTransactionRollbackOnPanic ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
defer func ( ) {
if r := recover ( ) ; r != nil {
str := r . ( string )
assert . EqualValues ( t , str , "Ooops" )
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , countFoos ( t , dbc2 ) )
}
} ( )
dbc . Transaction ( func ( dbs * db . DBC ) error {
dbs . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
assert . EqualValues ( t , 2 , countFoos ( t , dbc ) )
panic ( "Ooops" )
return nil
} )
}
func countFoos ( t * testing . T , dbc * db . DBC ) int {
var res int
err := dbc . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
return res
}
func TestTransactionRollback ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
{
_ , err := dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1),(2)" ) . Exec ( )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
}
var res int
{
err := dbc . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 2 , res )
}
dbc . Rollback ( )
{
err := dbc . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , res )
}
//let's try a fresh connection
2022-10-28 10:52:25 +03:00
dbc2 := getDBC ( p )
2022-10-26 17:28:42 +03:00
{
err := dbc2 . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , res )
}
}
func TestTransactionNestedRollback ( t * testing . T ) {
createFooTable ( t )
2022-10-28 10:52:25 +03:00
p := getPool ( )
dbc := getDBC ( p )
defer p . Close ( )
2022-10-26 17:28:42 +03:00
//begin 1
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
{
_ , err := dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1)" ) . Exec ( )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
}
var res int
{
err := dbc . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 1 , res )
}
//begin 2
2022-11-02 17:55:53 +03:00
require . Nil ( t , dbc . Begin ( ) )
2022-10-26 17:28:42 +03:00
{
_ , err := dbc . UpdateBySQL ( "INSERT INTO foo(id) VALUES(1)" ) . Exec ( )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
}
//no real rollback happens here since we are in a 'bigger' transaction
//rollback 2
dbc . Rollback ( )
{
err := dbc . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 2 , res )
}
//rollback 1
dbc . Rollback ( )
{
err := dbc . SelectBySQL ( "SELECT COUNT(id) FROM foo" ) . LoadValue ( & res )
2022-11-02 17:55:53 +03:00
require . Nil ( t , err )
2022-10-26 17:28:42 +03:00
assert . EqualValues ( t , 0 , res )
}
}