From efe529e62e52f9586b8c1091fd4e114cc3c87837 Mon Sep 17 00:00:00 2001 From: aradwann Date: Fri, 19 Jan 2024 21:14:36 +0200 Subject: [PATCH] test update user rpc --- gapi/main_test.go | 19 ++++ gapi/rpc_update_user_test.go | 200 +++++++++++++++++++++++++++++++++++ go.mod | 8 +- go.sum | 11 ++ 4 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 gapi/rpc_update_user_test.go diff --git a/gapi/main_test.go b/gapi/main_test.go index f12d18c..db97280 100644 --- a/gapi/main_test.go +++ b/gapi/main_test.go @@ -1,13 +1,17 @@ package gapi import ( + "context" + "fmt" "testing" "time" db "github.com/aradwann/eenergy/db/store" + "github.com/aradwann/eenergy/token" "github.com/aradwann/eenergy/util" "github.com/aradwann/eenergy/worker" "github.com/stretchr/testify/require" + "google.golang.org/grpc/metadata" ) func newTestServer(t *testing.T, store db.Store, taskDistributor worker.TaskDistributor) *Server { @@ -20,3 +24,18 @@ func newTestServer(t *testing.T, store db.Store, taskDistributor worker.TaskDist require.NoError(t, err) return server } + +func newNewContextWithBearerToken(t *testing.T, tokenMaker token.Maker, username string, duration time.Duration) context.Context { + ctx := context.Background() + accessToken, payload, err := tokenMaker.CreateToken(username, duration) + require.NoError(t, err) + require.NotNil(t, payload) + + bearerToken := fmt.Sprintf("%s %s", authorizationBearer, accessToken) + md := metadata.MD{ + authorizationHeader: []string{ + bearerToken, + }, + } + return metadata.NewIncomingContext(ctx, md) +} diff --git a/gapi/rpc_update_user_test.go b/gapi/rpc_update_user_test.go new file mode 100644 index 0000000..6fd3556 --- /dev/null +++ b/gapi/rpc_update_user_test.go @@ -0,0 +1,200 @@ +package gapi + +import ( + "context" + "database/sql" + "testing" + "time" + + mockdb "github.com/aradwann/eenergy/db/mock" + db "github.com/aradwann/eenergy/db/store" + "github.com/aradwann/eenergy/pb" + "github.com/aradwann/eenergy/token" + "github.com/aradwann/eenergy/util" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func TestUpdateUserAPI(t *testing.T) { + user, _ := randomUser(t) + + newName := util.RandomOwner() + newEmail := util.RandomEmail() + invalidEmail := "invalid-email" + + testCases := []struct { + name string + req *pb.UpdateUserRequest + buildStubs func(store *mockdb.MockStore) + buildContext func(t *testing.T, tokenMaker token.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(store *mockdb.MockStore) { + arg := db.UpdateUserParams{ + FullName: sql.NullString{ + String: newName, + Valid: true, + }, + Email: sql.NullString{ + String: newEmail, + Valid: true, + }, + Username: user.Username, + } + updatedUser := db.User{ + Username: user.Username, + HashedPassword: user.HashedPassword, + FullName: newName, + Email: newEmail, + PasswordChangedAt: user.PasswordChangedAt, + CreatedAt: user.CreatedAt, + IsEmailVerified: user.IsEmailVerified, + } + store.EXPECT(). + UpdateUser(gomock.Any(), gomock.Eq(arg)). + Times(1). + Return(updatedUser, nil) + + }, + buildContext: func(t *testing.T, tokenMaker token.Maker) context.Context { + return newNewContextWithBearerToken(t, tokenMaker, user.Username, 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(store *mockdb.MockStore) { + + store.EXPECT(). + UpdateUser(gomock.Any(), gomock.Any()). + Times(1). + Return(db.User{}, sql.ErrNoRows) + + }, + buildContext: func(t *testing.T, tokenMaker token.Maker) context.Context { + return newNewContextWithBearerToken(t, tokenMaker, user.Username, 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, codes.NotFound, st.Code()) + + }, + }, + { + name: "ExpiredToken", + req: &pb.UpdateUserRequest{ + Username: user.Username, + FullName: &newName, + Email: &newEmail, + }, + buildStubs: func(store *mockdb.MockStore) { + + store.EXPECT(). + UpdateUser(gomock.Any(), gomock.Any()). + Times(0) + + }, + buildContext: func(t *testing.T, tokenMaker token.Maker) context.Context { + return newNewContextWithBearerToken(t, tokenMaker, user.Username, -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, codes.Unauthenticated, st.Code()) + + }, + }, + { + name: "NoAuthorization", + req: &pb.UpdateUserRequest{ + Username: user.Username, + FullName: &newName, + Email: &invalidEmail, + }, + buildStubs: func(store *mockdb.MockStore) { + store.EXPECT(). + UpdateUser(gomock.Any(), gomock.Any()). + Times(0) + + }, + buildContext: func(t *testing.T, tokenMaker token.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, codes.Unauthenticated, st.Code()) + + }, + }, + { + name: "invalidEmail", + req: &pb.UpdateUserRequest{ + Username: user.Username, + FullName: &newName, + Email: &invalidEmail, + }, + buildStubs: func(store *mockdb.MockStore) { + + store.EXPECT(). + UpdateUser(gomock.Any(), gomock.Any()). + Times(0) + + }, + buildContext: func(t *testing.T, tokenMaker token.Maker) context.Context { + return newNewContextWithBearerToken(t, tokenMaker, user.Username, 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, codes.InvalidArgument, st.Code()) + + }, + }, + } + + for i := range testCases { + tc := testCases[i] + + t.Run(tc.name, func(t *testing.T) { + storeCtrl := gomock.NewController(t) + defer storeCtrl.Finish() + store := mockdb.NewMockStore(storeCtrl) + + tc.buildStubs(store) + server := newTestServer(t, store, nil) + + ctx := tc.buildContext(t, server.tokenMaker) + + res, err := server.UpdateUser(ctx, tc.req) + tc.checkResponse(t, res, err) + }) + } +} diff --git a/go.mod b/go.mod index e4b3ed1..3221ca6 100644 --- a/go.mod +++ b/go.mod @@ -14,8 +14,8 @@ require ( github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 golang.org/x/crypto v0.18.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 + google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac google.golang.org/grpc v1.60.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 google.golang.org/protobuf v1.32.0 @@ -41,7 +41,7 @@ require ( golang.org/x/net v0.20.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20240108191215-35c7eff3a6b1 // indirect + google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect ) require ( @@ -67,7 +67,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index a2f6208..6f59e51 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,7 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw= github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -100,6 +101,7 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -131,6 +133,7 @@ github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiw github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= @@ -268,6 +271,8 @@ golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -348,10 +353,16 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20240108191215-35c7eff3a6b1 h1:/IWabOtPziuXTEtI1KYCpM6Ss7vaAkeMxk+uXV/xvZs= google.golang.org/genproto v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 h1:OPXtXn7fNMaXwO3JvOmF1QyTc00jsSFFz1vXXBOdCDo= google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=