app-init
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
package navicat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"mysql-tunnel/internal/tunnel"
|
||||
)
|
||||
|
||||
func testClient(t *testing.T, opts ...MockOption) (*Client, *httptest.Server) {
|
||||
t.Helper()
|
||||
srv := httptest.NewServer(NewMockHandler(opts...))
|
||||
t.Cleanup(srv.Close)
|
||||
httpClient := tunnel.NewHTTPClient(tunnel.Options{Timeout: 5 * time.Second})
|
||||
c := NewClient(httpClient, ClientOptions{
|
||||
URL: srv.URL + "?token=secret",
|
||||
Target: Target{
|
||||
Host: "mysql",
|
||||
Port: 3306,
|
||||
User: "root",
|
||||
Password: "remote-pass",
|
||||
Database: "app",
|
||||
Charset: "utf8mb4",
|
||||
},
|
||||
})
|
||||
return c, srv
|
||||
}
|
||||
|
||||
func TestClientConnectAndSelect1(t *testing.T) {
|
||||
var cap CapturedRequest
|
||||
c, _ := testClient(t, WithToken("secret"), WithCapture(&cap))
|
||||
ctx := context.Background()
|
||||
|
||||
conn, err := c.Connect(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if conn.Conn.ServerInfo != "8.0.36-mock" {
|
||||
t.Fatalf("%+v", conn.Conn)
|
||||
}
|
||||
if cap.Action != ActionConnect || cap.Host != "mysql" || cap.Login != "root" {
|
||||
t.Fatalf("connect capture %+v", cap)
|
||||
}
|
||||
if cap.Port != "3306" {
|
||||
t.Fatalf("port %s", cap.Port)
|
||||
}
|
||||
if !cap.HasPassword {
|
||||
t.Fatal("password field missing")
|
||||
}
|
||||
|
||||
q, err := c.Query(ctx, "", "SELECT 1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, ok := LastResultSet(q)
|
||||
if !ok || res.Rows[0][0].Text() != "1" {
|
||||
t.Fatalf("select 1: %+v", q)
|
||||
}
|
||||
if cap.Action != ActionQuery {
|
||||
t.Fatalf("query capture %+v", cap)
|
||||
}
|
||||
joined := strings.Join(cap.Queries, " | ")
|
||||
if !strings.Contains(joined, "SET NAMES utf8mb4") {
|
||||
t.Fatalf("expected SET NAMES in %q", joined)
|
||||
}
|
||||
if !strings.Contains(joined, "SELECT 1") {
|
||||
t.Fatalf("expected SELECT 1 in %q", joined)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientTypesOverHTTP(t *testing.T) {
|
||||
c, _ := testClient(t, WithToken("secret"))
|
||||
ctx := context.Background()
|
||||
|
||||
q, err := c.Query(ctx, "app", "SELECT NULL AS n")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, _ := LastResultSet(q)
|
||||
if !res.Rows[0][0].Null {
|
||||
t.Fatal("NULL")
|
||||
}
|
||||
|
||||
q, err = c.Query(ctx, "", "SELECT bigint_val")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, _ = LastResultSet(q)
|
||||
got := res.Rows[0][0].Text()
|
||||
if got != "18446744073709551615" {
|
||||
t.Fatalf("bigint %q", got)
|
||||
}
|
||||
if _, err := strconv.ParseInt(got, 10, 64); err == nil {
|
||||
t.Fatal("test value should overflow int64")
|
||||
}
|
||||
|
||||
q, err = c.Query(ctx, "", "SELECT dec_val")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, _ = LastResultSet(q)
|
||||
if res.Fields[0].Type != 246 || res.Rows[0][0].Text() != "1234567890.123456789" {
|
||||
t.Fatalf("decimal %+v", res)
|
||||
}
|
||||
|
||||
q, err = c.Query(ctx, "", "SELECT zh_val")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, _ = LastResultSet(q)
|
||||
if res.Rows[0][0].Text() != "你好世界" {
|
||||
t.Fatalf("zh %q", res.Rows[0][0].Text())
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientSQLError(t *testing.T) {
|
||||
c, _ := testClient(t, WithToken("secret"))
|
||||
q, err := c.Query(context.Background(), "", "SELECT SYNTAX")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res, ok := LastResultSet(q)
|
||||
if !ok || !res.IsError() || res.Errno != 1064 {
|
||||
t.Fatalf("%+v", q)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientDML(t *testing.T) {
|
||||
c, _ := testClient(t, WithToken("secret"))
|
||||
q, err := c.Query(context.Background(), "", "UPDATE product SET price=1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res := q.Results[len(q.Results)-1]
|
||||
if res.IsResultSet() || res.AffectedRows != 1 || res.InsertID != 42 {
|
||||
t.Fatalf("%+v", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientBadTokenPOST(t *testing.T) {
|
||||
srv := httptest.NewServer(NewMockHandler(WithToken("right")))
|
||||
t.Cleanup(srv.Close)
|
||||
httpClient := tunnel.NewHTTPClient(tunnel.Options{Timeout: 5 * time.Second})
|
||||
c := NewClient(httpClient, ClientOptions{
|
||||
URL: srv.URL + "?token=wrong",
|
||||
Target: Target{Host: "mysql", Port: 3306, User: "root"},
|
||||
})
|
||||
_, err := c.Connect(context.Background())
|
||||
if err == nil {
|
||||
t.Fatal("expected token error")
|
||||
}
|
||||
ne, ok := err.(*Error)
|
||||
if !ok || ne.Errno != ErrnoInvalidToken {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientConnectDenied(t *testing.T) {
|
||||
c, _ := testClient(t, WithToken("secret"), WithConnectError(1045, "Access denied for user 'root'@'localhost'"))
|
||||
_, err := c.Connect(context.Background())
|
||||
if err == nil {
|
||||
t.Fatal("expected deny")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "1045") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "remote-pass") {
|
||||
t.Fatal("password leaked")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeBase64Query(t *testing.T) {
|
||||
var cap CapturedRequest
|
||||
srv := httptest.NewServer(NewMockHandler(WithToken("secret"), WithCapture(&cap)))
|
||||
t.Cleanup(srv.Close)
|
||||
httpClient := tunnel.NewHTTPClient(tunnel.Options{Timeout: 5 * time.Second})
|
||||
c := NewClient(httpClient, ClientOptions{
|
||||
URL: srv.URL + "?token=secret",
|
||||
Target: Target{Host: "mysql", Port: 3306, User: "root", Charset: "utf8"},
|
||||
EncodeBase64: true,
|
||||
})
|
||||
_, err := c.Query(context.Background(), "", "SELECT 1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cap.EncodeBase64 != "1" {
|
||||
t.Fatalf("%+v", cap)
|
||||
}
|
||||
if strings.Contains(strings.Join(cap.Queries, ""), "SELECT 1") {
|
||||
t.Fatalf("wire should be base64, got %v", cap.Queries)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user