From 0405baf87094dd0550f4ffcabdaa24df787f95d5 Mon Sep 17 00:00:00 2001 From: Sarthak160 Date: Wed, 9 Oct 2024 15:01:57 +0530 Subject: [PATCH 1/4] feat: add sample app for grpc client mocking Signed-off-by: Sarthak160 --- grpc-http-sample/README.md | 51 +++++ grpc-http-sample/go.mod | 15 ++ grpc-http-sample/go.sum | 14 ++ grpc-http-sample/grpc/example.proto | 17 ++ .../grpc/grpc-server/example/example.pb.go | 182 ++++++++++++++++++ .../grpc-server/example/example_grpc.pb.go | 121 ++++++++++++ grpc-http-sample/grpc/grpc-server/server.go | 34 ++++ grpc-http-sample/http/keploy.yml | 61 ++++++ grpc-http-sample/http/keploy/.gitignore | 2 + .../http/keploy/test-set-0/mocks.yaml | 119 ++++++++++++ .../http/keploy/test-set-0/tests/test-1.yaml | 38 ++++ .../http/keploy/test-set-0/tests/test-2.yaml | 40 ++++ .../http/keploy/test-set-0/tests/test-3.yaml | 40 ++++ grpc-http-sample/http/server.go | 54 ++++++ grpc-http-sample/keploy.yml | 61 ++++++ 15 files changed, 849 insertions(+) create mode 100644 grpc-http-sample/README.md create mode 100644 grpc-http-sample/go.mod create mode 100644 grpc-http-sample/go.sum create mode 100644 grpc-http-sample/grpc/example.proto create mode 100644 grpc-http-sample/grpc/grpc-server/example/example.pb.go create mode 100644 grpc-http-sample/grpc/grpc-server/example/example_grpc.pb.go create mode 100644 grpc-http-sample/grpc/grpc-server/server.go create mode 100755 grpc-http-sample/http/keploy.yml create mode 100644 grpc-http-sample/http/keploy/.gitignore create mode 100755 grpc-http-sample/http/keploy/test-set-0/mocks.yaml create mode 100755 grpc-http-sample/http/keploy/test-set-0/tests/test-1.yaml create mode 100755 grpc-http-sample/http/keploy/test-set-0/tests/test-2.yaml create mode 100755 grpc-http-sample/http/keploy/test-set-0/tests/test-3.yaml create mode 100644 grpc-http-sample/http/server.go create mode 100755 grpc-http-sample/keploy.yml diff --git a/grpc-http-sample/README.md b/grpc-http-sample/README.md new file mode 100644 index 0000000..9c80653 --- /dev/null +++ b/grpc-http-sample/README.md @@ -0,0 +1,51 @@ +# gRPC & HTTP Server Setup with Keploy + +This guide explains how to set up and run the gRPC and HTTP servers using Keploy to capture and test the HTTP calls. + +## Steps to Run + +### 1. Start the gRPC Server + +Open a terminal and navigate to the gRPC server directory: + +```bash +cd grpc/grpc-server +``` + +Run the gRPC server: + +```bash +go run server.go +``` + +### 2. Start the HTTP Server in Record Mode + +Open another terminal and navigate to the HTTP server directory: + +```bash +cd http +``` + +Build the HTTP server: + +```bash +go build -o httpserver +``` + +Run the HTTP server in record mode to capture the incoming HTTP calls and outgoing gRPC client calls: + +```bash +keploy record -c "httpserver" +``` + +### 3. Run the HTTP Server in Test Mode + +After recording, run the HTTP server in test mode to replay the captured calls: + +```bash +keploy test -c "httpserver" +``` + +--- + +This `README.md` provides a clear step-by-step guide for setting up and using your gRPC and HTTP servers with Keploy. Let me know if you'd like any changes! \ No newline at end of file diff --git a/grpc-http-sample/go.mod b/grpc-http-sample/go.mod new file mode 100644 index 0000000..afb5cef --- /dev/null +++ b/grpc-http-sample/go.mod @@ -0,0 +1,15 @@ +module github.com/Sarthak160/grpc-http-sample + +go 1.22.1 + +require ( + google.golang.org/grpc v1.67.1 + google.golang.org/protobuf v1.35.1 +) + +require ( + golang.org/x/net v0.28.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/text v0.17.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect +) diff --git a/grpc-http-sample/go.sum b/grpc-http-sample/go.sum new file mode 100644 index 0000000..a8432dc --- /dev/null +++ b/grpc-http-sample/go.sum @@ -0,0 +1,14 @@ +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= diff --git a/grpc-http-sample/grpc/example.proto b/grpc-http-sample/grpc/example.proto new file mode 100644 index 0000000..b0b5c66 --- /dev/null +++ b/grpc-http-sample/grpc/example.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package example; + +option go_package = "grpc-server/example;example"; // <- This line is important + +service ExampleService { + rpc SayHello (HelloRequest) returns (HelloResponse); +} + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string message = 1; +} diff --git a/grpc-http-sample/grpc/grpc-server/example/example.pb.go b/grpc-http-sample/grpc/grpc-server/example/example.pb.go new file mode 100644 index 0000000..471ebba --- /dev/null +++ b/grpc-http-sample/grpc/grpc-server/example/example.pb.go @@ -0,0 +1,182 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.1 +// protoc v3.21.12 +// source: example.proto + +package example + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + mi := &file_example_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_example_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_example_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type HelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *HelloResponse) Reset() { + *x = HelloResponse{} + mi := &file_example_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloResponse) ProtoMessage() {} + +func (x *HelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_example_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HelloResponse.ProtoReflect.Descriptor instead. +func (*HelloResponse) Descriptor() ([]byte, []int) { + return file_example_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_example_proto protoreflect.FileDescriptor + +var file_example_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x22, 0x22, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0d, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x4b, 0x0a, 0x0e, 0x45, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x08, 0x53, 0x61, 0x79, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x15, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x1d, 0x5a, 0x1b, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3b, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_example_proto_rawDescOnce sync.Once + file_example_proto_rawDescData = file_example_proto_rawDesc +) + +func file_example_proto_rawDescGZIP() []byte { + file_example_proto_rawDescOnce.Do(func() { + file_example_proto_rawDescData = protoimpl.X.CompressGZIP(file_example_proto_rawDescData) + }) + return file_example_proto_rawDescData +} + +var file_example_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_example_proto_goTypes = []any{ + (*HelloRequest)(nil), // 0: example.HelloRequest + (*HelloResponse)(nil), // 1: example.HelloResponse +} +var file_example_proto_depIdxs = []int32{ + 0, // 0: example.ExampleService.SayHello:input_type -> example.HelloRequest + 1, // 1: example.ExampleService.SayHello:output_type -> example.HelloResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_example_proto_init() } +func file_example_proto_init() { + if File_example_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_example_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_example_proto_goTypes, + DependencyIndexes: file_example_proto_depIdxs, + MessageInfos: file_example_proto_msgTypes, + }.Build() + File_example_proto = out.File + file_example_proto_rawDesc = nil + file_example_proto_goTypes = nil + file_example_proto_depIdxs = nil +} diff --git a/grpc-http-sample/grpc/grpc-server/example/example_grpc.pb.go b/grpc-http-sample/grpc/grpc-server/example/example_grpc.pb.go new file mode 100644 index 0000000..308d0b0 --- /dev/null +++ b/grpc-http-sample/grpc/grpc-server/example/example_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.21.12 +// source: example.proto + +package example + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + ExampleService_SayHello_FullMethodName = "/example.ExampleService/SayHello" +) + +// ExampleServiceClient is the client API for ExampleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ExampleServiceClient interface { + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) +} + +type exampleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewExampleServiceClient(cc grpc.ClientConnInterface) ExampleServiceClient { + return &exampleServiceClient{cc} +} + +func (c *exampleServiceClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HelloResponse) + err := c.cc.Invoke(ctx, ExampleService_SayHello_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ExampleServiceServer is the server API for ExampleService service. +// All implementations must embed UnimplementedExampleServiceServer +// for forward compatibility. +type ExampleServiceServer interface { + SayHello(context.Context, *HelloRequest) (*HelloResponse, error) + mustEmbedUnimplementedExampleServiceServer() +} + +// UnimplementedExampleServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedExampleServiceServer struct{} + +func (UnimplementedExampleServiceServer) SayHello(context.Context, *HelloRequest) (*HelloResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedExampleServiceServer) mustEmbedUnimplementedExampleServiceServer() {} +func (UnimplementedExampleServiceServer) testEmbeddedByValue() {} + +// UnsafeExampleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ExampleServiceServer will +// result in compilation errors. +type UnsafeExampleServiceServer interface { + mustEmbedUnimplementedExampleServiceServer() +} + +func RegisterExampleServiceServer(s grpc.ServiceRegistrar, srv ExampleServiceServer) { + // If the following call pancis, it indicates UnimplementedExampleServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&ExampleService_ServiceDesc, srv) +} + +func _ExampleService_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ExampleServiceServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ExampleService_SayHello_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ExampleServiceServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ExampleService_ServiceDesc is the grpc.ServiceDesc for ExampleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ExampleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "example.ExampleService", + HandlerType: (*ExampleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _ExampleService_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "example.proto", +} diff --git a/grpc-http-sample/grpc/grpc-server/server.go b/grpc-http-sample/grpc/grpc-server/server.go new file mode 100644 index 0000000..f0f4d81 --- /dev/null +++ b/grpc-http-sample/grpc/grpc-server/server.go @@ -0,0 +1,34 @@ +package main + +import ( + "context" + "fmt" + "log" + "net" + + "github.com/Sarthak160/grpc-http-sample/grpc/grpc-server/example" + "google.golang.org/grpc" +) + +type server struct { + example.UnimplementedExampleServiceServer +} + +func (s *server) SayHello(ctx context.Context, req *example.HelloRequest) (*example.HelloResponse, error) { + message := fmt.Sprintf("Hello, %s!", req.Name) + return &example.HelloResponse{Message: message}, nil +} + +func main() { + lis, err := net.Listen("tcp", ":50051") + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + + s := grpc.NewServer() + example.RegisterExampleServiceServer(s, &server{}) + log.Println("gRPC server running on :50051") + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} diff --git a/grpc-http-sample/http/keploy.yml b/grpc-http-sample/http/keploy.yml new file mode 100755 index 0000000..2d4a4ac --- /dev/null +++ b/grpc-http-sample/http/keploy.yml @@ -0,0 +1,61 @@ +path: "" +appId: 0 +appName: http +command: go run main.go +templatize: + testSets: [] +port: 0 +dnsPort: 26789 +proxyPort: 16789 +debug: false +disableTele: false +disableANSI: false +containerName: "" +networkName: "" +buildDelay: 30 +test: + selectedTests: {} + globalNoise: + global: {} + test-sets: {} + delay: 5 + host: "" + port: 0 + apiTimeout: 5 + skipCoverage: false + coverageReportPath: "" + ignoreOrdering: true + mongoPassword: default@123 + language: "" + removeUnusedMocks: false + fallBackOnMiss: false + jacocoAgentPath: "" + basePath: "" + mocking: true + ignoredTests: {} + disableLineCoverage: false + disableMockUpload: true + useLocalMock: false + updateTemplate: false +record: + filters: [] + recordTimer: 0s +configPath: "" +bypassRules: [] +generateGithubActions: false +keployContainer: keploy-v2 +keployNetwork: keploy-network +cmdType: native +contract: + services: [] + tests: [] + path: "" + download: false + generate: false + driven: consumer + mappings: + servicesMapping: {} + self: "" +inCi: false + +# Visit [https://keploy.io/docs/running-keploy/configuration-file/] to learn about using keploy through configration file. diff --git a/grpc-http-sample/http/keploy/.gitignore b/grpc-http-sample/http/keploy/.gitignore new file mode 100644 index 0000000..5137843 --- /dev/null +++ b/grpc-http-sample/http/keploy/.gitignore @@ -0,0 +1,2 @@ + +/reports/ diff --git a/grpc-http-sample/http/keploy/test-set-0/mocks.yaml b/grpc-http-sample/http/keploy/test-set-0/mocks.yaml new file mode 100755 index 0000000..4fa6e86 --- /dev/null +++ b/grpc-http-sample/http/keploy/test-set-0/mocks.yaml @@ -0,0 +1,119 @@ +version: api.keploy.io/v1beta1 +kind: gRPC +name: mock-0 +spec: + grpcReq: + headers: + pseudo_headers: + :authority: localhost:50051 + :method: POST + :path: /example.ExampleService/SayHello + :scheme: http + ordinary_headers: + content-type: application/grpc + grpc-timeout: 986616u + te: trailers + user-agent: grpc-go/1.67.1 + body: + compression_flag: 0 + message_length: 7 + decoded_data: | + 1: {"world"} + grpcResp: + headers: + pseudo_headers: + :status: "200" + ordinary_headers: + content-type: application/grpc + body: + compression_flag: 0 + message_length: 15 + decoded_data: | + 1: {"Hello, world!"} + trailers: + pseudo_headers: {} + ordinary_headers: + grpc-message: "" + grpc-status: "0" + reqTimestampMock: 2024-10-09T14:20:51.694192937+05:30 + resTimestampMock: 2024-10-09T14:20:51.699999378+05:30 +--- +version: api.keploy.io/v1beta1 +kind: gRPC +name: mock-1 +spec: + grpcReq: + headers: + pseudo_headers: + :authority: localhost:50051 + :method: POST + :path: /example.ExampleService/SayHello + :scheme: http + ordinary_headers: + content-type: application/grpc + grpc-timeout: 993335u + te: trailers + user-agent: grpc-go/1.67.1 + body: + compression_flag: 0 + message_length: 6 + decoded_data: | + 1: {"John"} + grpcResp: + headers: + pseudo_headers: + :status: "200" + ordinary_headers: + content-type: application/grpc + body: + compression_flag: 0 + message_length: 14 + decoded_data: | + 1: {"Hello, John!"} + trailers: + pseudo_headers: {} + ordinary_headers: + grpc-message: "" + grpc-status: "0" + reqTimestampMock: 2024-10-09T14:21:09.066179963+05:30 + resTimestampMock: 2024-10-09T14:21:09.067043986+05:30 +--- +version: api.keploy.io/v1beta1 +kind: gRPC +name: mock-2 +spec: + grpcReq: + headers: + pseudo_headers: + :authority: localhost:50051 + :method: POST + :path: /example.ExampleService/SayHello + :scheme: http + ordinary_headers: + content-type: application/grpc + grpc-timeout: 995726u + te: trailers + user-agent: grpc-go/1.67.1 + body: + compression_flag: 0 + message_length: 6 + decoded_data: | + 1: {"Jack"} + grpcResp: + headers: + pseudo_headers: + :status: "200" + ordinary_headers: + content-type: application/grpc + body: + compression_flag: 0 + message_length: 14 + decoded_data: | + 1: {"Hello, Jack!"} + trailers: + pseudo_headers: {} + ordinary_headers: + grpc-message: "" + grpc-status: "0" + reqTimestampMock: 2024-10-09T14:21:27.566671195+05:30 + resTimestampMock: 2024-10-09T14:21:27.5674101+05:30 diff --git a/grpc-http-sample/http/keploy/test-set-0/tests/test-1.yaml b/grpc-http-sample/http/keploy/test-set-0/tests/test-1.yaml new file mode 100755 index 0000000..8153985 --- /dev/null +++ b/grpc-http-sample/http/keploy/test-set-0/tests/test-1.yaml @@ -0,0 +1,38 @@ +version: api.keploy.io/v1beta1 +kind: Http +name: test-1 +spec: + metadata: {} + req: + method: GET + proto_major: 1 + proto_minor: 1 + url: http://localhost:8080/hello + header: + Accept: '*/*' + Host: localhost:8080 + User-Agent: curl/7.88.1 + body: "" + timestamp: 2024-10-09T14:20:51.671692498+05:30 + resp: + status_code: 200 + header: + Content-Length: "33" + Content-Type: text/plain; charset=utf-8 + Date: Wed, 09 Oct 2024 08:50:51 GMT + body: 'Response from gRPC: Hello, world!' + status_message: OK + proto_major: 0 + proto_minor: 0 + timestamp: 2024-10-09T14:20:53.729510274+05:30 + objects: [] + assertions: + noise: + header.Date: [] + created: 1728463853 +curl: | + curl --request GET \ + --url http://localhost:8080/hello \ + --header 'Accept: */*' \ + --header 'Host: localhost:8080' \ + --header 'User-Agent: curl/7.88.1' \ diff --git a/grpc-http-sample/http/keploy/test-set-0/tests/test-2.yaml b/grpc-http-sample/http/keploy/test-set-0/tests/test-2.yaml new file mode 100755 index 0000000..934da37 --- /dev/null +++ b/grpc-http-sample/http/keploy/test-set-0/tests/test-2.yaml @@ -0,0 +1,40 @@ +version: api.keploy.io/v1beta1 +kind: Http +name: test-2 +spec: + metadata: {} + req: + method: GET + proto_major: 1 + proto_minor: 1 + url: http://localhost:8080/hello?name=John + url_params: + name: John + header: + Accept: '*/*' + Host: localhost:8080 + User-Agent: curl/7.88.1 + body: "" + timestamp: 2024-10-09T14:21:09.053154872+05:30 + resp: + status_code: 200 + header: + Content-Length: "32" + Content-Type: text/plain; charset=utf-8 + Date: Wed, 09 Oct 2024 08:51:09 GMT + body: 'Response from gRPC: Hello, John!' + status_message: OK + proto_major: 0 + proto_minor: 0 + timestamp: 2024-10-09T14:21:11.105100712+05:30 + objects: [] + assertions: + noise: + header.Date: [] + created: 1728463871 +curl: | + curl --request GET \ + --url http://localhost:8080/hello?name=John \ + --header 'Host: localhost:8080' \ + --header 'User-Agent: curl/7.88.1' \ + --header 'Accept: */*' \ diff --git a/grpc-http-sample/http/keploy/test-set-0/tests/test-3.yaml b/grpc-http-sample/http/keploy/test-set-0/tests/test-3.yaml new file mode 100755 index 0000000..7043531 --- /dev/null +++ b/grpc-http-sample/http/keploy/test-set-0/tests/test-3.yaml @@ -0,0 +1,40 @@ +version: api.keploy.io/v1beta1 +kind: Http +name: test-3 +spec: + metadata: {} + req: + method: GET + proto_major: 1 + proto_minor: 1 + url: http://localhost:8080/hello?name=Jack + url_params: + name: Jack + header: + Accept: '*/*' + Host: localhost:8080 + User-Agent: curl/7.88.1 + body: "" + timestamp: 2024-10-09T14:21:27.560855552+05:30 + resp: + status_code: 200 + header: + Content-Length: "32" + Content-Type: text/plain; charset=utf-8 + Date: Wed, 09 Oct 2024 08:51:27 GMT + body: 'Response from gRPC: Hello, Jacky!' + status_message: OK + proto_major: 0 + proto_minor: 0 + timestamp: 2024-10-09T14:21:29.617937096+05:30 + objects: [] + assertions: + noise: + header.Date: [] + created: 1728463889 +curl: | + curl --request GET \ + --url http://localhost:8080/hello?name=Jack \ + --header 'Accept: */*' \ + --header 'Host: localhost:8080' \ + --header 'User-Agent: curl/7.88.1' \ diff --git a/grpc-http-sample/http/server.go b/grpc-http-sample/http/server.go new file mode 100644 index 0000000..3b63282 --- /dev/null +++ b/grpc-http-sample/http/server.go @@ -0,0 +1,54 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "time" + + "github.com/Sarthak160/grpc-http-sample/grpc/grpc-server/example" + "google.golang.org/grpc" +) + +// Call the gRPC service from the HTTP handler +func callGRPCService(name string) (string, error) { + conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) + if err != nil { + return "", fmt.Errorf("could not connect to gRPC server: %v", err) + } + defer conn.Close() + + client := example.NewExampleServiceClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + response, err := client.SayHello(ctx, &example.HelloRequest{Name: name}) + if err != nil { + return "", fmt.Errorf("could not call SayHello: %v", err) + } + + return response.Message, nil +} + +func handler(w http.ResponseWriter, r *http.Request) { + name := r.URL.Query().Get("name") + if name == "" { + name = "world" + } + + message, err := callGRPCService(name) + if err != nil { + http.Error(w, "Failed to call gRPC service", http.StatusInternalServerError) + return + } + + fmt.Fprintf(w, "Response from gRPC: %s", message) +} + +func main() { + http.HandleFunc("/hello", handler) + log.Println("HTTP server running on :8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/grpc-http-sample/keploy.yml b/grpc-http-sample/keploy.yml new file mode 100755 index 0000000..a1a1a2e --- /dev/null +++ b/grpc-http-sample/keploy.yml @@ -0,0 +1,61 @@ +path: "" +appId: 0 +appName: grpc_sample +command: go run main.go +templatize: + testSets: [] +port: 0 +dnsPort: 26789 +proxyPort: 16789 +debug: false +disableTele: false +disableANSI: false +containerName: "" +networkName: "" +buildDelay: 30 +test: + selectedTests: {} + globalNoise: + global: {} + test-sets: {} + delay: 5 + host: "" + port: 0 + apiTimeout: 5 + skipCoverage: false + coverageReportPath: "" + ignoreOrdering: true + mongoPassword: default@123 + language: "" + removeUnusedMocks: false + fallBackOnMiss: false + jacocoAgentPath: "" + basePath: "" + mocking: true + ignoredTests: {} + disableLineCoverage: false + disableMockUpload: true + useLocalMock: false + updateTemplate: false +record: + filters: [] + recordTimer: 0s +configPath: "" +bypassRules: [] +generateGithubActions: false +keployContainer: keploy-v2 +keployNetwork: keploy-network +cmdType: native +contract: + services: [] + tests: [] + path: "" + download: false + generate: false + driven: consumer + mappings: + servicesMapping: {} + self: "" +inCi: false + +# Visit [https://keploy.io/docs/running-keploy/configuration-file/] to learn about using keploy through configration file. From 1e87f1ca2aaf44001f5433b6a26c439dc0d195dc Mon Sep 17 00:00:00 2001 From: Sarthak Shyngle <50234097+Sarthak160@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:27:31 +0530 Subject: [PATCH 2/4] Update README.md --- grpc-http-sample/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/grpc-http-sample/README.md b/grpc-http-sample/README.md index 9c80653..ab3402e 100644 --- a/grpc-http-sample/README.md +++ b/grpc-http-sample/README.md @@ -38,6 +38,11 @@ Run the HTTP server in record mode to capture the incoming HTTP calls and outgoi keploy record -c "httpserver" ``` +Make a curl request to the http server : +```bash +curl "http://localhost:8080/hello?name=Jack" +``` + ### 3. Run the HTTP Server in Test Mode After recording, run the HTTP server in test mode to replay the captured calls: @@ -48,4 +53,4 @@ keploy test -c "httpserver" --- -This `README.md` provides a clear step-by-step guide for setting up and using your gRPC and HTTP servers with Keploy. Let me know if you'd like any changes! \ No newline at end of file +This `README.md` provides a clear step-by-step guide for setting up and using your gRPC and HTTP servers with Keploy. Let me know if you'd like any changes! From 9b8fb062e92eae813d2fa8dc7996268773778dbb Mon Sep 17 00:00:00 2001 From: Sarthak Shyngle <50234097+Sarthak160@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:34:00 +0530 Subject: [PATCH 3/4] Update README.md --- grpc-http-sample/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grpc-http-sample/README.md b/grpc-http-sample/README.md index ab3402e..aa69415 100644 --- a/grpc-http-sample/README.md +++ b/grpc-http-sample/README.md @@ -1,6 +1,7 @@ # gRPC & HTTP Server Setup with Keploy This guide explains how to set up and run the gRPC and HTTP servers using Keploy to capture and test the HTTP calls. +Here support grpc client (Unary rpc) has been showed, where keploy is recording incoming http calls and outgoing grpc calls. ## Steps to Run @@ -52,5 +53,3 @@ keploy test -c "httpserver" ``` --- - -This `README.md` provides a clear step-by-step guide for setting up and using your gRPC and HTTP servers with Keploy. Let me know if you'd like any changes! From 4368242e0b556f830bf9e86a5459f8b4ba433115 Mon Sep 17 00:00:00 2001 From: Sarthak Shyngle <50234097+Sarthak160@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:57:56 +0530 Subject: [PATCH 4/4] Update README.md --- grpc-http-sample/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/grpc-http-sample/README.md b/grpc-http-sample/README.md index aa69415..c8ab267 100644 --- a/grpc-http-sample/README.md +++ b/grpc-http-sample/README.md @@ -36,7 +36,7 @@ go build -o httpserver Run the HTTP server in record mode to capture the incoming HTTP calls and outgoing gRPC client calls: ```bash -keploy record -c "httpserver" +keploy record -c "./httpserver" ``` Make a curl request to the http server : @@ -49,7 +49,7 @@ curl "http://localhost:8080/hello?name=Jack" After recording, run the HTTP server in test mode to replay the captured calls: ```bash -keploy test -c "httpserver" +keploy test -c "./httpserver" ``` ---