diff --git a/server/test/resend_otp_test.go b/server/test/resend_otp_test.go new file mode 100644 index 0000000..3202d9e --- /dev/null +++ b/server/test/resend_otp_test.go @@ -0,0 +1,99 @@ +package test + +import ( + "context" + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/db" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/refs" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func resendOTPTest(t *testing.T, s TestSetup) { + t.Helper() + t.Run(`should verify otp`, func(t *testing.T) { + req, ctx := createContext(s) + email := "verify_otp." + s.TestInfo.Email + res, err := resolvers.SignupResolver(ctx, model.SignUpInput{ + Email: email, + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.NoError(t, err) + assert.NotNil(t, res) + + // Login should fail as email is not verified + loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + }) + assert.Error(t, err) + assert.Nil(t, loginRes) + verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeBasicAuthSignup) + assert.Nil(t, err) + assert.Equal(t, email, verificationRequest.Email) + verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{ + Token: verificationRequest.Token, + }) + assert.Nil(t, err) + assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty") + + // Using access token update profile + s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken)) + ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext) + _, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{ + IsMultiFactorAuthEnabled: refs.NewBoolRef(true), + }) + + // Resend otp should return error as no initial opt is being sent + resendOtpRes, err := resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{ + Email: email, + }) + assert.Error(t, err) + assert.Nil(t, resendOtpRes) + + // Login should not return error but access token should be empty as otp should have been sent + loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + }) + assert.NoError(t, err) + assert.NotNil(t, loginRes) + assert.Nil(t, loginRes.AccessToken) + + // Get otp from db + otp, err := db.Provider.GetOTPByEmail(ctx, email) + assert.NoError(t, err) + assert.NotEmpty(t, otp.Otp) + + // resend otp + resendOtpRes, err = resolvers.ResendOTPResolver(ctx, model.ResendOTPRequest{ + Email: email, + }) + assert.NoError(t, err) + assert.NotEmpty(t, resendOtpRes.Message) + + newOtp, err := db.Provider.GetOTPByEmail(ctx, email) + assert.NoError(t, err) + assert.NotEmpty(t, newOtp.Otp) + assert.NotEqual(t, otp.Otp, newOtp) + + // Should return error for older otp + verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ + Email: email, + Otp: otp.Otp, + }) + assert.Error(t, err) + + verifyOtpRes, err = resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ + Email: email, + Otp: newOtp.Otp, + }) + assert.NoError(t, err) + assert.NotEqual(t, verifyOtpRes.AccessToken, "", "access token should not be empty") + cleanData(email) + }) +} diff --git a/server/test/resolvers_test.go b/server/test/resolvers_test.go index a76522e..f10576e 100644 --- a/server/test/resolvers_test.go +++ b/server/test/resolvers_test.go @@ -115,6 +115,7 @@ func TestResolvers(t *testing.T) { inviteUserTest(t, s) validateJwtTokenTest(t, s) verifyOTPTest(t, s) + resendOTPTest(t, s) webhookLogsTest(t, s) // get logs after above resolver tests are done deleteWebhookTest(t, s) // delete webhooks (admin resolver) }) diff --git a/server/test/test.go b/server/test/test.go index b2288ff..1e5105b 100644 --- a/server/test/test.go +++ b/server/test/test.go @@ -57,6 +57,11 @@ func cleanData(email string) { err = db.Provider.DeleteVerificationRequest(ctx, verificationRequest) } + otp, err := db.Provider.GetOTPByEmail(ctx, email) + if err == nil { + err = db.Provider.DeleteOTP(ctx, otp) + } + dbUser, err := db.Provider.GetUserByEmail(ctx, email) if err == nil { db.Provider.DeleteUser(ctx, dbUser) diff --git a/server/test/verify_otp_test.go b/server/test/verify_otp_test.go index d236644..afb7e2e 100644 --- a/server/test/verify_otp_test.go +++ b/server/test/verify_otp_test.go @@ -1,12 +1,13 @@ package test import ( + "context" "testing" - "time" + "github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/db" - "github.com/authorizerdev/authorizer/server/db/models" "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/resolvers" "github.com/stretchr/testify/assert" ) @@ -14,7 +15,7 @@ import ( func verifyOTPTest(t *testing.T, s TestSetup) { t.Helper() t.Run(`should verify otp`, func(t *testing.T) { - _, ctx := createContext(s) + req, ctx := createContext(s) email := "verify_otp." + s.TestInfo.Email res, err := resolvers.SignupResolver(ctx, model.SignUpInput{ Email: email, @@ -23,20 +24,50 @@ func verifyOTPTest(t *testing.T, s TestSetup) { }) assert.NoError(t, err) assert.NotNil(t, res) - otp, err := db.Provider.UpsertOTP(ctx, &models.OTP{ - Otp: "123456", - Email: email, - ExpiresAt: time.Now().Add(1 * time.Minute).Unix(), - }) - assert.Equal(t, email, otp.Email) - assert.Nil(t, res.AccessToken, "access token should be nil") - verifyRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ - Otp: "123456", - Email: email, + // Login should fail as email is not verified + loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + }) + assert.Error(t, err) + assert.Nil(t, loginRes) + verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeBasicAuthSignup) + assert.Nil(t, err) + assert.Equal(t, email, verificationRequest.Email) + verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{ + Token: verificationRequest.Token, }) assert.Nil(t, err) assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty") + + // Using access token update profile + s.GinContext.Request.Header.Set("Authorization", "Bearer "+refs.StringValue(verifyRes.AccessToken)) + ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext) + _, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{ + IsMultiFactorAuthEnabled: refs.NewBoolRef(true), + }) + + // Login should not return error but access token should be empty as otp should have been sent + loginRes, err = resolvers.LoginResolver(ctx, model.LoginInput{ + Email: email, + Password: s.TestInfo.Password, + }) + assert.NoError(t, err) + assert.NotNil(t, loginRes) + assert.Nil(t, loginRes.AccessToken) + + // Get otp from db + otp, err := db.Provider.GetOTPByEmail(ctx, email) + assert.NoError(t, err) + assert.NotEmpty(t, otp.Otp) + + verifyOtpRes, err := resolvers.VerifyOtpResolver(ctx, model.VerifyOTPRequest{ + Email: email, + Otp: otp.Otp, + }) + assert.Nil(t, err) + assert.NotEqual(t, verifyOtpRes.AccessToken, "", "access token should not be empty") cleanData(email) }) }