397 lines
10 KiB
Go
397 lines
10 KiB
Go
package client
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/juju/errors"
|
|
. "github.com/pingcap/check"
|
|
"github.com/siddontang/go-mysql/test_util/test_keys"
|
|
|
|
"github.com/siddontang/go-mysql/mysql"
|
|
)
|
|
|
|
var testHost = flag.String("host", "127.0.0.1", "MySQL server host")
|
|
// We cover the whole range of MySQL server versions using docker-compose to bind them to different ports for testing.
|
|
// MySQL is constantly updating auth plugin to make it secure:
|
|
// starting from MySQL 8.0.4, a new auth plugin is introduced, causing plain password auth to fail with error:
|
|
// ERROR 1251 (08004): Client does not support authentication protocol requested by server; consider upgrading MySQL client
|
|
// Hint: use docker-compose to start corresponding MySQL docker containers and add the their ports here
|
|
var testPort = flag.String("port", "3306", "MySQL server port") // choose one or more form 5561,5641,3306,5722,8003,8012,8013, e.g. '3306,5722,8003'
|
|
var testUser = flag.String("user", "root", "MySQL user")
|
|
var testPassword = flag.String("pass", "", "MySQL password")
|
|
var testDB = flag.String("db", "test", "MySQL test database")
|
|
|
|
func Test(t *testing.T) {
|
|
segs := strings.Split(*testPort, ",")
|
|
for _, seg := range segs {
|
|
Suite(&clientTestSuite{port: seg})
|
|
}
|
|
TestingT(t)
|
|
}
|
|
|
|
type clientTestSuite struct {
|
|
c *Conn
|
|
port string
|
|
}
|
|
|
|
func (s *clientTestSuite) SetUpSuite(c *C) {
|
|
var err error
|
|
addr := fmt.Sprintf("%s:%s", *testHost, s.port)
|
|
s.c, err = Connect(addr, *testUser, *testPassword, "")
|
|
if err != nil {
|
|
c.Fatal(err)
|
|
}
|
|
|
|
_, err = s.c.Execute("CREATE DATABASE IF NOT EXISTS " + *testDB)
|
|
c.Assert(err, IsNil)
|
|
|
|
_, err = s.c.Execute("USE " + *testDB)
|
|
c.Assert(err, IsNil)
|
|
|
|
s.testConn_CreateTable(c)
|
|
s.testStmt_CreateTable(c)
|
|
}
|
|
|
|
func (s *clientTestSuite) TearDownSuite(c *C) {
|
|
if s.c == nil {
|
|
return
|
|
}
|
|
|
|
s.testConn_DropTable(c)
|
|
s.testStmt_DropTable(c)
|
|
|
|
if s.c != nil {
|
|
s.c.Close()
|
|
}
|
|
}
|
|
|
|
func (s *clientTestSuite) testConn_DropTable(c *C) {
|
|
_, err := s.c.Execute("drop table if exists mixer_test_conn")
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) testConn_CreateTable(c *C) {
|
|
str := `CREATE TABLE IF NOT EXISTS mixer_test_conn (
|
|
id BIGINT(64) UNSIGNED NOT NULL,
|
|
str VARCHAR(256),
|
|
f DOUBLE,
|
|
e enum("test1", "test2"),
|
|
u tinyint unsigned,
|
|
i tinyint,
|
|
PRIMARY KEY (id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8`
|
|
|
|
_, err := s.c.Execute(str)
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_Ping(c *C) {
|
|
err := s.c.Ping()
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
// NOTE for MySQL 5.5 and 5.6, server side has to config SSL to pass the TLS test, otherwise, it will throw error that
|
|
// MySQL server does not support TLS required by the client. However, for MySQL 5.7 and above, auto generated certificates
|
|
// are used by default so that manual config is no longer necessary.
|
|
func (s *clientTestSuite) TestConn_TLS_Verify(c *C) {
|
|
// Verify that the provided tls.Config is used when attempting to connect to mysql.
|
|
// An empty tls.Config will result in a connection error.
|
|
addr := fmt.Sprintf("%s:%s", *testHost, s.port)
|
|
_, err := Connect(addr, *testUser, *testPassword, *testDB, func(c *Conn) {
|
|
c.UseSSL(false)
|
|
})
|
|
if err == nil {
|
|
c.Fatal("expected error")
|
|
}
|
|
|
|
expected := "either ServerName or InsecureSkipVerify must be specified in the tls.Config"
|
|
if !strings.Contains(err.Error(), expected) {
|
|
c.Fatalf("expected '%s' to contain '%s'", err.Error(), expected)
|
|
}
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_TLS_Skip_Verify(c *C) {
|
|
// An empty tls.Config will result in a connection error but we can configure to skip it.
|
|
addr := fmt.Sprintf("%s:%s", *testHost, s.port)
|
|
_, err := Connect(addr, *testUser, *testPassword, *testDB, func(c *Conn) {
|
|
c.UseSSL(true)
|
|
})
|
|
c.Assert(err, Equals, nil)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_TLS_Certificate(c *C) {
|
|
// This test uses the TLS suite in 'go-mysql/docker/resources'. The certificates are not valid for any names.
|
|
// And if server uses auto-generated certificates, it will be an error like:
|
|
// "x509: certificate is valid for MySQL_Server_8.0.12_Auto_Generated_Server_Certificate, not not-a-valid-name"
|
|
tlsConfig := NewClientTLSConfig(test_keys.CaPem, test_keys.CertPem, test_keys.KeyPem, false, "not-a-valid-name")
|
|
addr := fmt.Sprintf("%s:%s", *testHost, s.port)
|
|
_, err := Connect(addr, *testUser, *testPassword, *testDB, func(c *Conn) {
|
|
c.SetTLSConfig(tlsConfig)
|
|
})
|
|
if err == nil {
|
|
c.Fatal("expected error")
|
|
}
|
|
if !strings.Contains(errors.Details(err), "certificate is not valid for any names") &&
|
|
!strings.Contains(errors.Details(err), "certificate is valid for") {
|
|
c.Fatalf("expected errors for server name verification, but got unknown error: %s", errors.Details(err))
|
|
}
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_Insert(c *C) {
|
|
str := `insert into mixer_test_conn (id, str, f, e) values(1, "a", 3.14, "test1")`
|
|
|
|
pkg, err := s.c.Execute(str)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(pkg.AffectedRows, Equals, uint64(1))
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_Select(c *C) {
|
|
str := `select str, f, e from mixer_test_conn where id = 1`
|
|
|
|
result, err := s.c.Execute(str)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(result.Fields, HasLen, 3)
|
|
c.Assert(result.Values, HasLen, 1)
|
|
|
|
ss, _ := result.GetString(0, 0)
|
|
c.Assert(ss, Equals, "a")
|
|
|
|
f, _ := result.GetFloat(0, 1)
|
|
c.Assert(f, Equals, float64(3.14))
|
|
|
|
e, _ := result.GetString(0, 2)
|
|
c.Assert(e, Equals, "test1")
|
|
|
|
ss, _ = result.GetStringByName(0, "str")
|
|
c.Assert(ss, Equals, "a")
|
|
|
|
f, _ = result.GetFloatByName(0, "f")
|
|
c.Assert(f, Equals, float64(3.14))
|
|
|
|
e, _ = result.GetStringByName(0, "e")
|
|
c.Assert(e, Equals, "test1")
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_Escape(c *C) {
|
|
e := `""''\abc`
|
|
str := fmt.Sprintf(`insert into mixer_test_conn (id, str) values(5, "%s")`,
|
|
mysql.Escape(e))
|
|
|
|
_, err := s.c.Execute(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
str = `select str from mixer_test_conn where id = ?`
|
|
|
|
r, err := s.c.Execute(str, 5)
|
|
c.Assert(err, IsNil)
|
|
|
|
ss, _ := r.GetString(0, 0)
|
|
c.Assert(ss, Equals, e)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestConn_SetCharset(c *C) {
|
|
err := s.c.SetCharset("gb2312")
|
|
c.Assert(err, IsNil)
|
|
|
|
err = s.c.SetCharset("utf8")
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) testStmt_DropTable(c *C) {
|
|
str := `drop table if exists mixer_test_stmt`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Execute()
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) testStmt_CreateTable(c *C) {
|
|
str := `CREATE TABLE IF NOT EXISTS mixer_test_stmt (
|
|
id BIGINT(64) UNSIGNED NOT NULL,
|
|
str VARCHAR(256),
|
|
f DOUBLE,
|
|
e enum("test1", "test2"),
|
|
u tinyint unsigned,
|
|
i tinyint,
|
|
PRIMARY KEY (id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Execute()
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_Delete(c *C) {
|
|
str := `delete from mixer_test_stmt`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Execute()
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_Insert(c *C) {
|
|
str := `insert into mixer_test_stmt (id, str, f, e, u, i) values (?, ?, ?, ?, ?, ?)`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
r, err := stmt.Execute(1, "a", 3.14, "test1", 255, -127)
|
|
c.Assert(err, IsNil)
|
|
|
|
c.Assert(r.AffectedRows, Equals, uint64(1))
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_Select(c *C) {
|
|
str := `select str, f, e from mixer_test_stmt where id = ?`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
result, err := stmt.Execute(1)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(result.Values, HasLen, 1)
|
|
c.Assert(result.Fields, HasLen, 3)
|
|
|
|
ss, _ := result.GetString(0, 0)
|
|
c.Assert(ss, Equals, "a")
|
|
|
|
f, _ := result.GetFloat(0, 1)
|
|
c.Assert(f, Equals, float64(3.14))
|
|
|
|
e, _ := result.GetString(0, 2)
|
|
c.Assert(e, Equals, "test1")
|
|
|
|
ss, _ = result.GetStringByName(0, "str")
|
|
c.Assert(ss, Equals, "a")
|
|
|
|
f, _ = result.GetFloatByName(0, "f")
|
|
c.Assert(f, Equals, float64(3.14))
|
|
|
|
e, _ = result.GetStringByName(0, "e")
|
|
c.Assert(e, Equals, "test1")
|
|
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_NULL(c *C) {
|
|
str := `insert into mixer_test_stmt (id, str, f, e) values (?, ?, ?, ?)`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
result, err := stmt.Execute(2, nil, 3.14, nil)
|
|
c.Assert(err, IsNil)
|
|
|
|
c.Assert(result.AffectedRows, Equals, uint64(1))
|
|
|
|
stmt.Close()
|
|
|
|
str = `select * from mixer_test_stmt where id = ?`
|
|
stmt, err = s.c.Prepare(str)
|
|
defer stmt.Close()
|
|
|
|
c.Assert(err, IsNil)
|
|
|
|
result, err = stmt.Execute(2)
|
|
b, err := result.IsNullByName(0, "id")
|
|
c.Assert(err, IsNil)
|
|
c.Assert(b, Equals, false)
|
|
|
|
b, err = result.IsNullByName(0, "str")
|
|
c.Assert(err, IsNil)
|
|
c.Assert(b, Equals, true)
|
|
|
|
b, err = result.IsNullByName(0, "f")
|
|
c.Assert(err, IsNil)
|
|
c.Assert(b, Equals, false)
|
|
|
|
b, err = result.IsNullByName(0, "e")
|
|
c.Assert(err, IsNil)
|
|
c.Assert(b, Equals, true)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_Unsigned(c *C) {
|
|
str := `insert into mixer_test_stmt (id, u) values (?, ?)`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
defer stmt.Close()
|
|
|
|
result, err := stmt.Execute(3, uint8(255))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(result.AffectedRows, Equals, uint64(1))
|
|
|
|
str = `select u from mixer_test_stmt where id = ?`
|
|
|
|
stmt, err = s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
defer stmt.Close()
|
|
|
|
result, err = stmt.Execute(3)
|
|
c.Assert(err, IsNil)
|
|
|
|
u, err := result.GetUint(0, 0)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(u, Equals, uint64(255))
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_Signed(c *C) {
|
|
str := `insert into mixer_test_stmt (id, i) values (?, ?)`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Execute(4, 127)
|
|
c.Assert(err, IsNil)
|
|
|
|
_, err = stmt.Execute(uint64(18446744073709551516), int8(-128))
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *clientTestSuite) TestStmt_Trans(c *C) {
|
|
_, err := s.c.Execute(`insert into mixer_test_stmt (id, str) values (1002, "abc")`)
|
|
c.Assert(err, IsNil)
|
|
|
|
err = s.c.Begin()
|
|
c.Assert(err, IsNil)
|
|
|
|
str := `select str from mixer_test_stmt where id = ?`
|
|
|
|
stmt, err := s.c.Prepare(str)
|
|
c.Assert(err, IsNil)
|
|
|
|
defer stmt.Close()
|
|
|
|
_, err = stmt.Execute(1002)
|
|
c.Assert(err, IsNil)
|
|
|
|
err = s.c.Commit()
|
|
c.Assert(err, IsNil)
|
|
|
|
r, err := stmt.Execute(1002)
|
|
c.Assert(err, IsNil)
|
|
|
|
str, _ = r.GetString(0, 0)
|
|
c.Assert(str, Equals, `abc`)
|
|
} |