diff --git a/test/bdchain/api/README.md b/test/bdchain/api/README.md index e69de29..140a7b5 100644 --- a/test/bdchain/api/README.md +++ b/test/bdchain/api/README.md @@ -0,0 +1,21 @@ +# API 测试工具 + +## Go 版本测试服务器 `mockserver` + +### 编译 + +1. 准备好 Go 环境 +2. 下载 [BDChain Go SDK](https://phabricator.internetapi.cn/source/bdchain/repository/master/) 到 `GOPATH` +3. 切换工作目录到 `mockserver` 目录 +4. 使用 `go get -d ./...` 安装此项目依赖 +5. 使用 `go build` 编译 +6. `mockserver` 即为编译好的可执行程序 + +### 运行 + +- 使用 `.\mockserver --help` 查看使用帮助 +- 目前仅支持使用 `--port N` 设定程序运行的端口为 N + +### 测试连接 + +在需要测试连接的位置,设定连接地址和端口为此 `mockserver` 所在地址及端口即可。 diff --git a/test/bdchain/api/mockserver/.gitignore b/test/bdchain/api/mockserver/.gitignore new file mode 100644 index 0000000..5188726 --- /dev/null +++ b/test/bdchain/api/mockserver/.gitignore @@ -0,0 +1,6 @@ +# Go +debug +debug.test + +mockserver +mockserver.exe diff --git a/test/bdchain/api/mockserver/errors.go b/test/bdchain/api/mockserver/errors.go new file mode 100644 index 0000000..134915c --- /dev/null +++ b/test/bdchain/api/mockserver/errors.go @@ -0,0 +1,52 @@ +package main + +import ( + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Errors +var ( + ErrInvalidServer = errors.New("server is invalid") + ErrInvalidRequest = errors.New("request is invalid") + ErrInvalidParameter = errors.New("parameter is invalid") + ErrTestNotImplemented = errors.New("test path not implemented") + + ErrMultiple = errors.New("Multiple errors in transaction") + ErrEmptyFrom = errors.New("from must not be empty") + ErrInvalidTo = errors.New("to must be a valid 32-byte address") + ErrEmptyName = errors.New("name must not be empty") +) + +func toStatusError(src error) error { + /*__*/ if src == nil { + return src + } else if _, ok := src.(interface{ GRPCStatus() *status.Status }); ok { + return src + } else { + switch errors.Cause(src) { + ///////////////////////////////////////////////////////////////////// + default: + return src + case ErrInvalidServer: + fallthrough + case ErrInvalidRequest: + return status.Error(codes.Internal, src.Error()) + case ErrInvalidParameter: + return status.Error(codes.InvalidArgument, src.Error()) + case ErrTestNotImplemented: + return status.Error(codes.Unimplemented, src.Error()) + + ///////////////////////////////////////////////////////////////////// + case ErrEmptyName: + fallthrough + case ErrEmptyFrom: + fallthrough + case ErrInvalidTo: + return status.Error(codes.InvalidArgument, src.Error()) + case ErrMultiple: + return status.Error(codes.InvalidArgument, src.Error()) + } + } +} diff --git a/test/bdchain/api/mockserver/main.go b/test/bdchain/api/mockserver/main.go new file mode 100644 index 0000000..65689d1 --- /dev/null +++ b/test/bdchain/api/mockserver/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "flag" + "fmt" + "net" + "strconv" + + "bdchain/api/grpc/txledger" + + "google.golang.org/grpc" +) + +func main() { + // flag + port := flag.Int("port", 10000, "set the port of test server") + flag.Parse() + + // server + rpc := grpc.NewServer() + srv := (*grpcServer)(&server{}) + txledger.RegisterTransactionLedgerServer(rpc, srv) + + con, err := net.Listen("tcp", ":"+strconv.Itoa(*port)) + if err == nil { + fmt.Println("server is running on", *port) + } else { + fmt.Println(err) + return + } + + // start + err = rpc.Serve(con) + if err != nil { + fmt.Println("server closed with error", err) + } else { + fmt.Println("server closed") + } +} diff --git a/test/bdchain/api/mockserver/server.go b/test/bdchain/api/mockserver/server.go new file mode 100644 index 0000000..51f0bcb --- /dev/null +++ b/test/bdchain/api/mockserver/server.go @@ -0,0 +1,191 @@ +package main + +import ( + "bytes" + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "bdchain/api/grpc/common" + "bdchain/api/grpc/txledger" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes/empty" +) + +///////////////////////////////////////////////////////////////////////////// +// server +type server struct{} + +///////////////////////////////////////////////////////////////////////////// +// grpcServer +type grpcServer server + +func (s *grpcServer) ClientVersion( + c context.Context, + r *empty.Empty, +) (res *common.ClientVersionResponse, err error) { + switch { + case s == nil: + err = toStatusError(ErrInvalidServer) + case r == nil: + err = toStatusError(ErrInvalidRequest) + default: + res = &common.ClientVersionResponse{ + Version: "TxLedgerGo/v0.0.1alpha/darwin/go1.11", + } + } + return +} + +func (s *grpcServer) CreateLedger( + c context.Context, + r *txledger.CreateLedgerRequest, +) (res *txledger.CreateLedgerResponse, err error) { + switch { + case s == nil: + err = toStatusError(ErrInvalidServer) + case r == nil: + err = toStatusError(ErrInvalidRequest) + case r.Name == "": + err = toStatusError(ErrEmptyName) + case r.Name == "test": + res = &txledger.CreateLedgerResponse{ + Ok: true, + } + default: + err = toStatusError(ErrTestNotImplemented) + } + return +} + +func (s *grpcServer) GetLedgers( + c context.Context, + r *empty.Empty, +) (res *txledger.GetLedgersResponse, err error) { + switch { + case s == nil: + err = toStatusError(ErrInvalidServer) + case r == nil: + err = toStatusError(ErrInvalidRequest) + default: + res = &txledger.GetLedgersResponse{ + Ledgers: []string{ + "first", + "second", + "third", + }, + } + } + return +} + +func (s *grpcServer) SendTransaction( + c context.Context, + r *txledger.SendTransactionRequest, +) (res *txledger.SendTransactionResponse, err error) { + switch { + case s == nil: + err = toStatusError(ErrInvalidServer) + case r == nil: + fallthrough + case r.Transaction == nil: + err = toStatusError(ErrInvalidRequest) + case r.Ledger != "test": + err = toStatusError(ErrTestNotImplemented) + default: + type Input struct { + Type common.TransactionType + From []byte + To []byte + Data []byte + } + type Output struct { + Hash []byte + Error error + } + type Case struct { + I Input + O Output + } + + Merge := func( + p *status.Status, + cs ...*status.Status, + ) *status.Status { + msg := []proto.Message{} + for _, c := range cs { + msg = append(msg, c.Proto()) + } + r, err := p.WithDetails(msg...) + if err != nil { + panic(err) + } + return r + } + + tx := r.Transaction + for _, c := range [...]Case{ + { + I: Input{ + Type: common.TransactionType_MESSAGE, + From: []byte{ + 0xf0, 0x0d, 0xca, 0xfe, 0xf0, 0x0d, 0xca, 0xfe, + 0xf0, 0x0d, 0xca, 0xfe, 0xf0, 0x0d, 0xca, 0xfe, + 0xf0, 0x0d, 0xca, 0xfe, + }, + To: []byte{ + 0xfe, 0xed, 0xba, 0xbe, 0xfe, 0xed, 0xba, 0xbe, + 0xfe, 0xed, 0xba, 0xbe, 0xfe, 0xed, 0xba, 0xbe, + 0xfe, 0xed, 0xba, 0xbe, + }, + Data: []byte{ + 0xde, 0xad, 0xbe, 0xef, + }, + }, + O: Output{ + Hash: []byte{ + 0xd1, 0x5e, 0xa5, 0xed, 0xd1, 0x5e, 0xa5, 0xed, + 0xd1, 0x5e, 0xa5, 0xed, 0xd1, 0x5e, 0xa5, 0xed, + 0xd1, 0x5e, 0xa5, 0xed, 0xd1, 0x5e, 0xa5, 0xed, + 0xd1, 0x5e, 0xa5, 0xed, 0xd1, 0x5e, 0xa5, 0xed, + }, + Error: nil, + }, + }, + { + I: Input{ + Type: common.TransactionType_MESSAGE, + From: nil, + To: []byte{ + 0x50, 0xba, 0xda, 0x55, + }, + Data: nil, + }, + O: Output{ + Hash: nil, + Error: Merge( + status.New(codes.InvalidArgument, ErrMultiple.Error()), + status.New(codes.InvalidArgument, ErrEmptyFrom.Error()), + status.New(codes.InvalidArgument, ErrInvalidTo.Error()), + ).Err(), + }, + }, + } { + if c.I.Type == tx.Type && + bytes.Equal(c.I.From, tx.From) && + bytes.Equal(c.I.To, tx.To) && + bytes.Equal(c.I.Data, tx.Data) { + if c.O.Error != nil { + err = c.O.Error + } else { + res = &txledger.SendTransactionResponse{Hash: c.O.Hash} + } + return + } + } + err = toStatusError(ErrTestNotImplemented) + } + return +}