micro-tests/server/combo/combo_test.go

210 lines
5.9 KiB
Go
Raw Permalink Normal View History

package combo_test
import (
"context"
"embed"
"fmt"
"io"
"io/fs"
"net/http"
"strings"
"testing"
grpccli "go.unistack.org/micro-client-grpc/v3"
httpcli "go.unistack.org/micro-client-http/v3"
jsonpbcodec "go.unistack.org/micro-codec-jsonpb/v3"
protocodec "go.unistack.org/micro-codec-proto/v3"
grpcsrv "go.unistack.org/micro-server-grpc/v3"
httpsrv "go.unistack.org/micro-server-http/v3"
mgpb "go.unistack.org/micro-tests/server/combo/mgpb"
mhpb "go.unistack.org/micro-tests/server/combo/mhpb"
ngpb "go.unistack.org/micro-tests/server/combo/ngpb"
pb "go.unistack.org/micro-tests/server/combo/proto"
"go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/register"
"go.unistack.org/micro/v3/server"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
//go:embed swagger-ui
var assets embed.FS
type Handler struct {
t *testing.T
}
const (
grpcDefaultContentType = "application/grpc+proto"
httpDefaultContentType = "application/json"
)
func newComboMux(httph http.Handler, grpch http.Handler, drpch http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor == 2 {
ct := r.Header.Get("content-type")
switch {
case strings.HasPrefix(ct, "application/grpc"):
if grpch != nil {
grpch.ServeHTTP(w, r)
}
return
case strings.HasPrefix(ct, "application/drpc"):
if drpch != nil {
drpch.ServeHTTP(w, r)
}
return
}
}
httph.ServeHTTP(w, r)
})
}
func (h *Handler) Call(ctx context.Context, req *pb.CallReq, rsp *pb.CallRsp) error {
rsp.Rsp = "name_my_name"
return nil
}
func TestComboServer(t *testing.T) {
reg := register.NewRegister()
ctx := context.Background()
h := &Handler{t: t}
_ = logger.DefaultLogger.Init(logger.WithCallerSkipCount(3))
// create grpc server
gsrv := grpcsrv.NewServer(
server.Name("helloworld"),
server.Register(reg),
server.Codec("application/json", jsonpbcodec.NewCodec()),
server.Codec("application/grpc", protocodec.NewCodec()),
server.Codec("application/grpc+proto", protocodec.NewCodec()),
server.Codec("application/grpc+json", jsonpbcodec.NewCodec()),
)
// init grpc server
if err := gsrv.Init(); err != nil {
t.Fatalf("grpc err: %v", err)
}
if err := mgpb.RegisterTestServer(gsrv, h); err != nil {
t.Fatalf("grpc err: %v", err)
}
swaggerdir, _ := fs.Sub(assets, "swagger-ui")
// create http server
hsrv := httpsrv.NewServer(
server.Address("127.0.0.1:0"),
server.Name("helloworld"),
server.Register(reg),
server.Codec("application/json", jsonpbcodec.NewCodec()),
httpsrv.PathHandler(http.MethodGet, "/swagger-ui/*", http.StripPrefix("/swagger-ui", http.FileServer(http.FS(swaggerdir))).ServeHTTP),
)
// fill http server handler struct
hs := &http.Server{Handler: h2c.NewHandler(newComboMux(hsrv, gsrv.GRPCServer(), nil), &http2.Server{})}
// init http server
if err := hsrv.Init(httpsrv.Server(hs)); err != nil {
t.Fatal(err)
}
if err := mhpb.RegisterTestServer(hsrv, h); err != nil {
t.Fatalf("grpc err: %v", err)
}
// start http server
if err := hsrv.Start(); err != nil {
t.Fatal(err)
}
// lookup server
service, err := reg.LookupService(ctx, "helloworld")
if err != nil {
t.Fatal(err)
}
if len(service) != 1 {
t.Fatalf("Expected 1 service got %d: %+v", len(service), service)
}
if len(service[0].Nodes) != 1 {
t.Fatalf("Expected 1 node got %d: %+v", len(service[0].Nodes), service[0].Nodes)
}
mhcli := client.NewClientCallOptions(httpcli.NewClient(client.ContentType(httpDefaultContentType), client.Codec(httpDefaultContentType, jsonpbcodec.NewCodec())), client.WithAddress("http://"+service[0].Nodes[0].Address))
mhttpsvc := mhpb.NewTestClient("helloworld", mhcli)
mgcli := client.NewClientCallOptions(grpccli.NewClient(client.ContentType(grpcDefaultContentType), client.Codec(grpcDefaultContentType, protocodec.NewCodec())), client.WithAddress("http://"+service[0].Nodes[0].Address))
mgrpcsvc := mgpb.NewTestClient("helloworld", mgcli)
t.Logf("call via micro grpc")
if rsp, err := mgrpcsvc.Call(ctx, &pb.CallReq{Req: "my_name"}); err != nil {
t.Fatal(err)
} else if rsp.Rsp != "name_my_name" {
t.Fatalf("invalid response: %#+v\n", rsp)
}
ngcli, err := grpc.DialContext(ctx, service[0].Nodes[0].Address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatal(err)
}
defer ngcli.Close()
ngrpcsvc := ngpb.NewTestClient(ngcli)
t.Logf("call via native grpc")
if rsp, err := ngrpcsvc.Call(ctx, &ngpb.CallReq{Req: "my_name"}); err != nil {
t.Fatal(err)
} else if rsp.Rsp != "name_my_name" {
t.Fatalf("invalid response: %#+v\n", rsp)
}
t.Logf("call via micro http")
if rsp, err := mhttpsvc.Call(ctx, &pb.CallReq{Req: "my_name"}); err != nil {
t.Fatal(err)
} else if rsp.Rsp != "name_my_name" {
t.Fatalf("invalid response: %#+v\n", rsp)
}
var hreq *http.Request
hreq, err = http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("http://%s/swagger-ui/index.html", service[0].Nodes[0].Address), nil)
if err != nil {
t.Fatal(err)
}
var hrsp *http.Response
hrsp, err = http.DefaultClient.Do(hreq)
if err != nil || hrsp.StatusCode != http.StatusOK {
t.Fatalf("error rsp: %v err: %v", hrsp, err)
}
hreq, err = http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("http://%s/Call", service[0].Nodes[0].Address), strings.NewReader(`{"req":"my_name"}`))
if err != nil {
t.Fatal(err)
}
hreq.Header.Add("Content-Type", "application/json")
hrsp, err = http.DefaultClient.Do(hreq)
if err != nil || hrsp.StatusCode != http.StatusOK {
t.Fatalf("error rsp: %v err: %v", hrsp, err)
}
defer hrsp.Body.Close()
buf, err := io.ReadAll(hrsp.Body)
if err != nil {
t.Fatalf("read body fail: %v", err)
}
rsp := &pb.CallRsp{}
if err = jsonpbcodec.NewCodec().Unmarshal(buf, rsp); err != nil {
t.Fatal(err)
} else if rsp.Rsp != "name_my_name" {
t.Fatalf("invalid response: %#+v\n", rsp)
}
}