package gapi import ( "context" "testing" "time" "github.com/FernandoJVideira/api_tmpl/internal/auth" db "github.com/FernandoJVideira/api_tmpl/internal/db/sqlc" "github.com/FernandoJVideira/api_tmpl/internal/service" mocksvc "github.com/FernandoJVideira/api_tmpl/internal/service/mock" "github.com/FernandoJVideira/api_tmpl/pb" "github.com/FernandoJVideira/api_tmpl/pkg/testutil" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestUpdateUserRPC(t *testing.T) { user, _ := randomUser(t) newName := testutil.RandomOwner() newEmail := testutil.RandomEmail() invalidEmail := "invalid-email-format" testCases := []struct { name string req *pb.UpdateUserRequest buildStubs func(userService *mocksvc.MockUserService) buildContext func(t *testing.T, tokenMaker auth.Maker) context.Context checkResponse func(t *testing.T, res *pb.UpdateUserResponse, err error) }{ { name: "OK", req: &pb.UpdateUserRequest{ Username: user.Username, FullName: &newName, Email: &newEmail, }, buildStubs: func(userService *mocksvc.MockUserService) { arg := service.UserUpdateRequest{ Username: user.Username, FullName: &newName, Email: &newEmail, } updatedUser := db.User{ Username: user.Username, HashedPassword: user.HashedPassword, FullName: newName, Email: newEmail, } userService.EXPECT(). UpdateUser(gomock.Any(), gomock.Eq(arg)). Times(1). Return(&updatedUser, nil) }, buildContext: func(t *testing.T, tokenMaker auth.Maker) context.Context { return newContextWithBearerToken(t, tokenMaker, user.Username, user.Role, time.Minute) }, checkResponse: func(t *testing.T, res *pb.UpdateUserResponse, err error) { require.NoError(t, err) require.NotNil(t, res) updatedUser := res.GetUser() require.Equal(t, user.Username, updatedUser.Username) require.Equal(t, newName, updatedUser.FullName) require.Equal(t, newEmail, updatedUser.Email) }, }, { name: "UserNotFound", req: &pb.UpdateUserRequest{ Username: user.Username, FullName: &newName, Email: &newEmail, }, buildStubs: func(userService *mocksvc.MockUserService) { userService.EXPECT(). UpdateUser(gomock.Any(), gomock.Any()). Times(1). Return(nil, db.ErrRecordNotFound) }, buildContext: func(t *testing.T, tokenMaker auth.Maker) context.Context { return newContextWithBearerToken(t, tokenMaker, user.Username, user.Role, time.Minute) }, checkResponse: func(t *testing.T, res *pb.UpdateUserResponse, err error) { require.Error(t, err) st, ok := status.FromError(err) require.True(t, ok) require.Equal(t, st.Code(), codes.NotFound) }, }, { name: "ExpiredToken", req: &pb.UpdateUserRequest{ Username: user.Username, FullName: &newName, Email: &newEmail, }, buildStubs: func(userService *mocksvc.MockUserService) { userService.EXPECT(). UpdateUser(gomock.Any(), gomock.Any()). Times(0) }, buildContext: func(t *testing.T, tokenMaker auth.Maker) context.Context { return newContextWithBearerToken(t, tokenMaker, user.Username, user.Role, -time.Minute) }, checkResponse: func(t *testing.T, res *pb.UpdateUserResponse, err error) { require.Error(t, err) st, ok := status.FromError(err) require.True(t, ok) require.Equal(t, st.Code(), codes.Unauthenticated) }, }, { name: "NoAuthorizationHeader", req: &pb.UpdateUserRequest{ Username: user.Username, FullName: &newName, Email: &newEmail, }, buildStubs: func(userService *mocksvc.MockUserService) { userService.EXPECT(). UpdateUser(gomock.Any(), gomock.Any()). Times(0) }, buildContext: func(t *testing.T, tokenMaker auth.Maker) context.Context { return context.Background() }, checkResponse: func(t *testing.T, res *pb.UpdateUserResponse, err error) { require.Error(t, err) st, ok := status.FromError(err) require.True(t, ok) require.Equal(t, st.Code(), codes.Unauthenticated) }, }, { name: "InvalidEmail", req: &pb.UpdateUserRequest{ Username: user.Username, FullName: &newName, Email: &invalidEmail, }, buildStubs: func(userService *mocksvc.MockUserService) { userService.EXPECT(). UpdateUser(gomock.Any(), gomock.Any()). Times(0) }, buildContext: func(t *testing.T, tokenMaker auth.Maker) context.Context { return newContextWithBearerToken(t, tokenMaker, user.Username, user.Role, time.Minute) }, checkResponse: func(t *testing.T, res *pb.UpdateUserResponse, err error) { require.Error(t, err) st, ok := status.FromError(err) require.True(t, ok) require.Equal(t, st.Code(), codes.InvalidArgument) }, }, } for i := range testCases { tc := testCases[i] t.Run(tc.name, func(t *testing.T) { userCtrl := gomock.NewController(t) defer userCtrl.Finish() userService := mocksvc.NewMockUserService(userCtrl) tc.buildStubs(userService) // Start test server and send request s := newTestServer(t, nil, nil, userService, nil) ctx := tc.buildContext(t, s.tokenMaker) // Call UpdateUser RPC res, err := s.UpdateUser(ctx, tc.req) tc.checkResponse(t, res, err) }) } }