util/test: add helper funcs #212
@ -1,25 +0,0 @@
|
|||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_NewSQLRowsFromFile(t *testing.T) {
|
|
||||||
db, c, err := sqlmock.New()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
rows, err := NewSQLRowsFromFile(c, "testdata/Call.csv")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !strings.Contains(fmt.Sprintf("%#+v", rows), `cols:[]string{"DepAgrId", "DepAgrNum", "DepAgrDate", "DepAgrCloseDate", "AccCur", "MainFinaccNum", "MainFinaccName", "MainFinaccId", "MainFinaccOpenDt", "DepAgrStatus", "MainFinaccBal", "DepartCode", "CardAccId"}`) {
|
|
||||||
t.Fatal("invalid cols after import csv")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +1,65 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/DATA-DOG/go-sqlmock"
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
"go.unistack.org/micro/v4/client"
|
"go.unistack.org/micro/v4/client"
|
||||||
"go.unistack.org/micro/v4/codec"
|
"go.unistack.org/micro/v4/codec"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getExt(name string) string {
|
||||||
|
ext := filepath.Ext(name)
|
||||||
|
if len(ext) > 0 && ext[0] == '.' {
|
||||||
|
ext = ext[1:]
|
||||||
|
}
|
||||||
|
return ext
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNameWithoutExt(name string) string {
|
||||||
|
return strings.TrimSuffix(name, filepath.Ext(name))
|
||||||
|
}
|
||||||
|
|
||||||
var ErrUnknownContentType = errors.New("unknown content type")
|
var ErrUnknownContentType = errors.New("unknown content type")
|
||||||
|
|
||||||
type Extension struct {
|
type Extension struct {
|
||||||
Ext []string
|
Ext []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var ExtToTypes = map[string][]string{
|
var (
|
||||||
|
ExtToTypes = map[string][]string{
|
||||||
"json": {"application/json", "application/grpc+json"},
|
"json": {"application/json", "application/grpc+json"},
|
||||||
"yaml": {"application/yaml", "application/yml", "text/yaml", "text/yml"},
|
"yaml": {"application/yaml", "application/yml", "text/yaml", "text/yml"},
|
||||||
"yml": {"application/yaml", "application/yml", "text/yaml", "text/yml"},
|
"yml": {"application/yaml", "application/yml", "text/yaml", "text/yml"},
|
||||||
"proto": {"application/grpc", "application/grpc+proto", "application/proto"},
|
"proto": {"application/grpc", "application/grpc+proto", "application/proto"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefaultExts = []string{"csv", "json", "yaml", "yml", "proto"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func clientCall(ctx context.Context, c client.Client, req client.Request, rsp interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResponseFromFile(rspfile string) (*codec.Frame, error) {
|
||||||
|
rspbuf, err := os.ReadFile(rspfile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &codec.Frame{Data: rspbuf}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewRequestFromFile(c client.Client, reqfile string) (client.Request, error) {
|
func NewRequestFromFile(c client.Client, reqfile string) (client.Request, error) {
|
||||||
reqbuf, err := os.ReadFile(reqfile)
|
reqbuf, err := os.ReadFile(reqfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -32,10 +67,7 @@ func NewRequestFromFile(c client.Client, reqfile string) (client.Request, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
endpoint := path.Base(path.Dir(reqfile))
|
endpoint := path.Base(path.Dir(reqfile))
|
||||||
ext := path.Ext(reqfile)
|
ext := getExt(reqfile)
|
||||||
if len(ext) > 0 && ext[0] == '.' {
|
|
||||||
ext = ext[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
var ct string
|
var ct string
|
||||||
if cts, ok := ExtToTypes[ext]; ok {
|
if cts, ok := ExtToTypes[ext]; ok {
|
||||||
@ -56,26 +88,189 @@ func NewRequestFromFile(c client.Client, reqfile string) (client.Request, error)
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLRowsFromFile(c sqlmock.Sqlmock, file string) (*sqlmock.Rows, error) {
|
func SQLFromFile(m sqlmock.Sqlmock, name string) error {
|
||||||
fp, err := os.Open(file)
|
fp, err := os.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
|
return SQLFromReader(m, fp)
|
||||||
|
}
|
||||||
|
|
||||||
r := csv.NewReader(fp)
|
func SQLFromBytes(m sqlmock.Sqlmock, buf []byte) error {
|
||||||
r.Comma = '\t'
|
return SQLFromReader(m, bytes.NewReader(buf))
|
||||||
r.Comment = '#'
|
}
|
||||||
|
|
||||||
records, err := r.ReadAll()
|
func SQLFromString(m sqlmock.Sqlmock, buf string) error {
|
||||||
|
return SQLFromReader(m, strings.NewReader(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
func SQLFromReader(m sqlmock.Sqlmock, r io.Reader) error {
|
||||||
|
var rows *sqlmock.Rows
|
||||||
|
var exp *sqlmock.ExpectedQuery
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
|
||||||
|
for {
|
||||||
|
s, err := br.ReadString('\n')
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
} else if err == io.EOF && len(s) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[0] != '#' {
|
||||||
|
r := csv.NewReader(strings.NewReader(s))
|
||||||
|
r.Comma = ','
|
||||||
|
var records [][]string
|
||||||
|
records, err = r.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
|
}
|
||||||
|
if rows == nil {
|
||||||
|
rows = m.NewRows(records[0])
|
||||||
|
} else {
|
||||||
|
for idx := 0; idx < len(records); idx++ {
|
||||||
|
rows.FromCSVString(strings.Join(records[idx], ","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := c.NewRows(records[0])
|
if rows != nil {
|
||||||
for idx := 1; idx < len(records); idx++ {
|
exp.WillReturnRows(rows)
|
||||||
rows.FromCSVString(strings.Join(records[idx], ";"))
|
rows = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows, nil
|
switch {
|
||||||
|
case strings.HasPrefix(strings.ToLower(s[2:]), "begin"):
|
||||||
|
m.ExpectBegin()
|
||||||
|
case strings.HasPrefix(strings.ToLower(s[2:]), "commit"):
|
||||||
|
m.ExpectCommit()
|
||||||
|
case strings.HasPrefix(strings.ToLower(s[2:]), "rollback"):
|
||||||
|
m.ExpectRollback()
|
||||||
|
case strings.HasPrefix(strings.ToLower(s[2:]), "exec "):
|
||||||
|
m.ExpectExec(s[2+len("exec "):])
|
||||||
|
case strings.HasPrefix(strings.ToLower(s[2:]), "query "):
|
||||||
|
exp = m.ExpectQuery(s[2+len("query "):])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunWithClientExpectResults(ctx context.Context, c client.Client, m sqlmock.Sqlmock, dir string, exts []string) error {
|
||||||
|
tcases, err := getFiles(dir, exts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g, gctx := errgroup.WithContext(ctx)
|
||||||
|
if !strings.Contains(dir, "parallel") {
|
||||||
|
g.SetLimit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tcase := range tcases {
|
||||||
|
for _, dbfile := range tcase.dbfiles {
|
||||||
|
if err = SQLFromFile(m, dbfile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := 0; idx < len(tcase.reqfiles); idx++ {
|
||||||
|
g.TryGo(func() error {
|
||||||
|
req, err := NewRequestFromFile(c, tcase.reqfiles[idx])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rsp, err := NewResponseFromFile(tcase.rspfiles[idx])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := &codec.Frame{}
|
||||||
|
err = c.Call(gctx, req, data, client.WithContentType(req.ContentType()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(rsp.Data, data.Data) {
|
||||||
|
return fmt.Errorf("rsp not equal test %s != %s", rsp.Data, data.Data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return g.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunWithClientExpectErrors(ctx context.Context, c client.Client, dir string) error {
|
||||||
|
g, gctx := errgroup.WithContext(ctx)
|
||||||
|
if !strings.Contains(dir, "parallel") {
|
||||||
|
g.SetLimit(1)
|
||||||
|
}
|
||||||
|
_ = gctx
|
||||||
|
g.TryGo(func() error {
|
||||||
|
// rsp := &codec.Frame{}
|
||||||
|
// return c.Call(ctx, req, rsp, client.WithContentType(req.ContentType()))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return g.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Case struct {
|
||||||
|
dbfiles []string
|
||||||
|
reqfiles []string
|
||||||
|
rspfiles []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFiles(dir string, exts []string) ([]Case, error) {
|
||||||
|
var tcases []Case
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if len(entries) == 0 && err != nil {
|
||||||
|
return tcases, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if exts == nil {
|
||||||
|
exts = DefaultExts
|
||||||
|
}
|
||||||
|
|
||||||
|
var dirs []string
|
||||||
|
var dbfiles, reqfiles, rspfiles []string
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
dirs = append(dirs, filepath.Join(dir, entry.Name()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if info, err := entry.Info(); err != nil {
|
||||||
|
return tcases, err
|
||||||
|
} else if !info.Mode().IsRegular() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ext := range exts {
|
||||||
|
if getExt(entry.Name()) == ext {
|
||||||
|
name := getNameWithoutExt(entry.Name())
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(name, "_db"):
|
||||||
|
dbfiles = append(dbfiles, filepath.Join(dir, entry.Name()))
|
||||||
|
case strings.HasSuffix(name, "_req"):
|
||||||
|
reqfiles = append(reqfiles, filepath.Join(dir, entry.Name()))
|
||||||
|
case strings.HasSuffix(name, "_rsp"):
|
||||||
|
rspfiles = append(rspfiles, filepath.Join(dir, entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reqfiles) > 0 && len(rspfiles) > 0 {
|
||||||
|
tcases = append(tcases, Case{dbfiles: dbfiles, reqfiles: reqfiles, rspfiles: rspfiles})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir = range dirs {
|
||||||
|
ntcases, err := getFiles(dir, exts)
|
||||||
|
if len(ntcases) == 0 && err != nil {
|
||||||
|
return tcases, err
|
||||||
|
} else if len(ntcases) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tcases = append(tcases, ntcases...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tcases, nil
|
||||||
}
|
}
|
||||||
|
72
util/test/test_test.go
Normal file
72
util/test/test_test.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_SQLFromFile(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
db, c, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if err = SQLFromFile(c, "testdata/result/01_firstcase/Call_db.csv"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := tx.QueryContext(ctx, "select * from test;")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
var id int64
|
||||||
|
var name string
|
||||||
|
err = rows.Scan(&id, &name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if id != 1 || name != "test" {
|
||||||
|
t.Fatalf("invalid rows %v %v", id, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = rows.Close(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = c.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getFiles(t *testing.T) {
|
||||||
|
files, err := getFiles("testdata/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
t.Fatalf("no files matching")
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := len(files); n != 1 {
|
||||||
|
t.Fatalf("invalid number of test cases %d", n)
|
||||||
|
}
|
||||||
|
}
|
5
util/test/testdata/result/01_firstcase/Call_db.csv
vendored
Normal file
5
util/test/testdata/result/01_firstcase/Call_db.csv
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# begin
|
||||||
|
# query select \* from test;
|
||||||
|
id,name
|
||||||
|
1,test
|
||||||
|
# commit
|
|
1
util/test/testdata/result/01_firstcase/Call_req.json
vendored
Normal file
1
util/test/testdata/result/01_firstcase/Call_req.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
1
util/test/testdata/result/01_firstcase/Call_rsp.json
vendored
Normal file
1
util/test/testdata/result/01_firstcase/Call_rsp.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
Loading…
Reference in New Issue
Block a user