package handlers import ( "context" "net/http" "otaupdate/services" "testing" "github.com/fiskerinc/cloud-services/pkg/grpc/sms" "github.com/fiskerinc/cloud-services/pkg/httpclient/tester" th "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/pkg/errors" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) type MockSmsClientSucc struct{} // HandleSMSQueue implements sms.SMSServiceClient. func (*MockSmsClientSucc) HandleSMSQueue(ctx context.Context, in *sms.SendSMSRequest, opts ...grpc.CallOption) (*sms.SMSQueueResponse, error) { return &sms.SMSQueueResponse{ SmsMsgID: "1023", SentSuccessful: false, }, nil } func (m *MockSmsClientSucc) HandleSMSSend(ctx context.Context,in *sms.SendSMSRequest,opts ...grpc.CallOption,) (*sms.SMSDetailsResponse, error) { return &sms.SMSDetailsResponse{ SmsMsgID: "123", Status: 5, MessageText: "messageText", SenderLogin: "sender", SentFrom: "from", SentTo: "to", DateSent: "dateSent", MsgType: "type", DateModified: "dateModified", ICCID: "iccid", }, nil } func (m *MockSmsClientSucc) HandleGetProducts(ctx context.Context, in *sms.GetAvailableProductsRequest, opts ...grpc.CallOption) (*sms.GetAvailableProductsResponse, error) { return nil, nil } func (m *MockSmsClientSucc) HandleChangeRatePlan(ctx context.Context, in *sms.ChangeRatePlanRequest, opts ...grpc.CallOption) (*sms.ChangeRatePlanResponse, error) { return nil, nil } func (m *MockSmsClientSucc) HandleCustomAttributes(ctx context.Context, in *sms.CustomAtributesRequest, opts ...grpc.CallOption) (*sms.CustomAtributeResponse, error) { return nil, nil } func (m *MockSmsClientSucc) HandleDeviceDetails(ctx context.Context, in *sms.DeviceDetailsRequest, opts ...grpc.CallOption) (*sms.DeviceDetailsResponse, error) { return nil, nil } type MockSmsClientFail struct{} // HandleSMSQueue implements sms.SMSServiceClient. func (*MockSmsClientFail) HandleSMSQueue(ctx context.Context, in *sms.SendSMSRequest, opts ...grpc.CallOption) (*sms.SMSQueueResponse, error) { return &sms.SMSQueueResponse{ SmsMsgID: "1023", SentSuccessful: false, }, nil } func (m *MockSmsClientFail) HandleSMSSend( ctx context.Context, in *sms.SendSMSRequest, opts ...grpc.CallOption, ) (*sms.SMSDetailsResponse, error) { return nil, errors.New("unintelligable error") } func (m *MockSmsClientFail) HandleGetProducts(ctx context.Context, in *sms.GetAvailableProductsRequest, opts ...grpc.CallOption) (*sms.GetAvailableProductsResponse, error) { return nil, nil } func (m *MockSmsClientFail) HandleChangeRatePlan(ctx context.Context, in *sms.ChangeRatePlanRequest, opts ...grpc.CallOption) (*sms.ChangeRatePlanResponse, error) { return nil, nil } func (m *MockSmsClientFail) HandleCustomAttributes(ctx context.Context, in *sms.CustomAtributesRequest, opts ...grpc.CallOption) (*sms.CustomAtributeResponse, error) { return nil, nil } func (m *MockSmsClientFail) HandleDeviceDetails(ctx context.Context, in *sms.DeviceDetailsRequest, opts ...grpc.CallOption) (*sms.DeviceDetailsResponse, error) { return nil, nil } type MockSmsClientFailWithGoodErr struct{} // HandleSMSQueue implements sms.SMSServiceClient. func (*MockSmsClientFailWithGoodErr) HandleSMSQueue(ctx context.Context, in *sms.SendSMSRequest, opts ...grpc.CallOption) (*sms.SMSQueueResponse, error) { return &sms.SMSQueueResponse{ SmsMsgID: "1023", SentSuccessful: false, }, nil } func (m *MockSmsClientFailWithGoodErr) HandleSMSSend( ctx context.Context, in *sms.SendSMSRequest, opts ...grpc.CallOption, ) (*sms.SMSDetailsResponse, error) { return nil, status.Error(codes.Internal, "bad message status") } func (m *MockSmsClientFailWithGoodErr) HandleGetProducts(ctx context.Context, in *sms.GetAvailableProductsRequest, opts ...grpc.CallOption) (*sms.GetAvailableProductsResponse, error) { return nil, nil } func (m *MockSmsClientFailWithGoodErr) HandleChangeRatePlan(ctx context.Context, in *sms.ChangeRatePlanRequest, opts ...grpc.CallOption) (*sms.ChangeRatePlanResponse, error) { return nil, nil } func (m *MockSmsClientFailWithGoodErr) HandleCustomAttributes(ctx context.Context, in *sms.CustomAtributesRequest, opts ...grpc.CallOption) (*sms.CustomAtributeResponse, error) { return nil, nil } func (m *MockSmsClientFailWithGoodErr) HandleDeviceDetails(ctx context.Context, in *sms.DeviceDetailsRequest, opts ...grpc.CallOption) (*sms.DeviceDetailsResponse, error) { return nil, nil } func Test_HandleSMSSend(t *testing.T) { type testCase struct { name string doBefore func() httpTC tester.HttpTestCase } testCases := []testCase{ { name: "Valid", doBefore: func() { services.SetSmsClient(&MockSmsClientSucc{}) }, httpTC: tester.HttpTestCase{ Request: th.MakeTestRequest( "POST", "http://example.com/sms", sms.SendSMSRequest{ICCID: "iccid", MessageText: "messageText"}), ExpectedStatus: http.StatusOK, ExpectedRegexMap: map[string]string{"smsMsgID": "123", "status": "Delivered", "messageText": "messageText", "senderLogin": "sender", "sentTo": "to", "sentFrom": "from", "msgType": "type", "dateSent": "dateSent", "dateModified": "dateModified", "ICCID": "iccid"}, }, }, { name: "InvalidJson", doBefore: func() { services.SetSmsClient(&MockSmsClientSucc{}) }, httpTC: tester.HttpTestCase{ Request: th.MakeTestRequest( "POST", "http://example.com/sms", "invalid"), ExpectedStatus: http.StatusBadRequest, ExpectedResponse: `{"message":"json: cannot unmarshal string into Go value of type sms.SendSMSRequest","error":"Bad Request"}`, }, }, { name: "Failing with bad error", doBefore: func() { services.SetSmsClient(&MockSmsClientFail{}) }, httpTC: tester.HttpTestCase{ Request: th.MakeTestRequest( "POST", "http://example.com/sms", sms.SendSMSRequest{ICCID: "iccid", MessageText: "messageText"}), ExpectedStatus: http.StatusInternalServerError, ExpectedResponse: `{"message":"unintelligable error","error":"Internal Server Error"}`, }, }, { name: "Failing with good error", doBefore: func() { services.SetSmsClient(&MockSmsClientFailWithGoodErr{}) }, httpTC: tester.HttpTestCase{ Request: th.MakeTestRequest( "POST", "http://example.com/sms", sms.SendSMSRequest{ICCID: "iccid", MessageText: "messageText"}), ExpectedStatus: http.StatusExpectationFailed, ExpectedResponse: `{"message":"bad message status","error":"Expectation Failed"}`, }, }, } for _, tc := range testCases { tc.doBefore() w := tc.httpTC.Test(HandleSMSSend) tc.httpTC.ValidateHttp(t, tc.name, w) } } func Test_grpcErrToHttpErr(t *testing.T) { type args struct { status *status.Status } tests := []struct { name string args args wantCode int wantMsg string }{ { name: "Internal", args: args{ status: status.New(codes.Internal, "just"), }, wantCode: 500, wantMsg: "just", }, { name: "Internal with bad status", args: args{ status: status.New(codes.Internal, "bad message status"), }, wantCode: http.StatusExpectationFailed, wantMsg: "bad message status", }, { name: "InvalidArgument", args: args{ status: status.New(codes.InvalidArgument, "just"), }, wantCode: 400, wantMsg: "just", }, { name: "NotFound", args: args{ status: status.New(codes.NotFound, "just"), }, wantCode: 404, wantMsg: "just", }, { name: "AlreadyExists", args: args{ status: status.New(codes.AlreadyExists, "just"), }, wantCode: 409, wantMsg: "just", }, { name: "PermissionDenied", args: args{ status: status.New(codes.PermissionDenied, "just"), }, wantCode: 403, wantMsg: "just", }, { name: "Unauthenticated", args: args{ status: status.New(codes.Unauthenticated, "just"), }, wantCode: 401, wantMsg: "just", }, { name: "Unimplemented", args: args{ status: status.New(codes.Unimplemented, "just"), }, wantCode: 501, wantMsg: "just", }, { name: "Unknown", args: args{ status: status.New(codes.Unknown, "just"), }, wantCode: 500, wantMsg: "just", }, { name: "Unavailable", args: args{ status: status.New(codes.Unavailable, "just"), }, wantCode: 503, wantMsg: "just", }, { name: "DeadlineExceeded", args: args{ status: status.New(codes.DeadlineExceeded, "just"), }, wantCode: http.StatusRequestTimeout, wantMsg: "just", }, { name: "Canceled", args: args{ status: status.New(codes.Canceled, "just"), }, wantCode: 499, wantMsg: "just", }, { name: "FailedPrecondition", args: args{ status: status.New(codes.FailedPrecondition, "just"), }, wantCode: 412, wantMsg: "just", }, { name: "OutOfRange", args: args{ status: status.New(codes.OutOfRange, "just"), }, wantCode: 400, wantMsg: "just", }, { name: "Aborted", args: args{ status: status.New(codes.Aborted, "just"), }, wantCode: 409, wantMsg: "just", }, { name: "ResourceExhausted", args: args{ status: status.New(codes.ResourceExhausted, "just"), }, wantCode: 429, wantMsg: "just", }, { name: "DataLoss", args: args{ status: status.New(codes.DataLoss, "just"), }, wantCode: http.StatusInternalServerError, wantMsg: "just", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if gotMsg, gotCode := grpcErrToHttpErr(tt.args.status); gotCode != tt.wantCode || gotMsg != tt.wantMsg { t.Errorf("grpcErrToHttpErr() = (%v, %v), want (%v, %v)", gotCode, gotMsg, tt.wantCode, tt.wantMsg) } }) } }