Skip to content

Commit

Permalink
implement code for user challenge test sms
Browse files Browse the repository at this point in the history
  • Loading branch information
erudenko committed Jul 10, 2023
1 parent 66db835 commit 1e90177
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 14 deletions.
20 changes: 16 additions & 4 deletions services/sms/mock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,27 @@ package mock
import "fmt"

// SMSServiceMock mocks SMS service.
type SMSServiceMock struct{}
type SMSService struct {
Messages []string
Recipients []string
}

// NewSMSService returns pointer to newly created SMS service mock.
func NewSMSService() (*SMSServiceMock, error) {
return &SMSServiceMock{}, nil
func NewSMSService() (*SMSService, error) {
return &SMSService{}, nil
}

// SendSMS implements SMSService.
func (ss *SMSServiceMock) SendSMS(recipient, message string) error {
func (ss *SMSService) SendSMS(recipient, message string) error {
fmt.Printf("📱: MOCK SMS SERVICE: Sending SMS: \nrecipient: %s\nmessage: %s\n\n", recipient, message)
ss.Messages = append(ss.Messages, message)
ss.Recipients = append(ss.Recipients, recipient)
return nil
}

func (ss *SMSService) Last() (string, string) {
if len(ss.Messages) == 0 {
return "", ""
}
return ss.Messages[len(ss.Messages)-1], ss.Recipients[len(ss.Recipients)-1]
}
15 changes: 11 additions & 4 deletions storage/mock/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ import (
"golang.org/x/exp/maps"
)

var _a model.AppStorage = &App{}
var (
ErrNotFound = errors.New("not found")
ErrNotImplemented = errors.New("not implemented")

_a model.AppStorage = &App{}
)

// App is mock implementation of model.AppStorage interface.
// ! User it for tests only!
type App struct {
Apps map[string]model.AppData
}
Expand All @@ -28,7 +35,7 @@ func (a *App) AppByID(id string) (model.AppData, error) {
return app, nil
}
}
return model.AppData{}, errors.New("not found")
return model.AppData{}, ErrNotFound
}

func (a *App) ActiveAppByID(appID string) (model.AppData, error) {
Expand All @@ -37,7 +44,7 @@ func (a *App) ActiveAppByID(appID string) (model.AppData, error) {
return model.AppData{}, err
}
if !app.Active {
return model.AppData{}, errors.New("not found")
return model.AppData{}, ErrNotFound
}
return app, nil
}
Expand Down Expand Up @@ -76,7 +83,7 @@ func (a *App) DeleteApp(id string) error {

func (a *App) ImportJSON(data []byte, cleanOldData bool) error {
// TODO: implement
return errors.New("not implemented")
return ErrNotImplemented
}

func (a *App) TestDatabaseConnection() error {
Expand Down
17 changes: 17 additions & 0 deletions storage/mock/storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package mock

import "context"

type Storage struct{}

func (u *Storage) Ready(ctx context.Context) error {
return nil
}

func (u *Storage) Connect(ctx context.Context) error {
return nil
}

func (u *Storage) Close(ctx context.Context) error {
return nil
}
26 changes: 26 additions & 0 deletions storage/mock/user_auth_storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package mock

import (
"context"

"github.com/madappgang/identifo/v2/model"
)

type UserAuthStorage struct {
Storage
Challenges []model.UserAuthChallenge
}

func (u *UserAuthStorage) ImportJSON(data []byte, clearOldData bool) error {
return nil
}

func (u *UserAuthStorage) AddChallenge(ctx context.Context, challenge model.UserAuthChallenge) (model.UserAuthChallenge, error) {
u.Challenges = append(u.Challenges, challenge)
return challenge, nil
}

func (u *UserAuthStorage) GetLatestChallenge(ctx context.Context, strategy model.AuthStrategy, userID string) (model.UserAuthChallenge, error) {
// just return the last one
return u.Challenges[len(u.Challenges)-1], nil
}
97 changes: 91 additions & 6 deletions storage/user_controller_challenges_test.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,114 @@
package storage_test
package storage

import (
"context"
"errors"
"fmt"
"testing"

"github.com/madappgang/identifo/v2/l"
"github.com/madappgang/identifo/v2/model"
"github.com/madappgang/identifo/v2/storage"
smock "github.com/madappgang/identifo/v2/services/sms/mock"
"github.com/madappgang/identifo/v2/storage/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/text/language"
)

func TestRequestSMSTest(t *testing.T) {
cc := storage.UserStorageController{}
func TestRequestSMSTestNoApp(t *testing.T) {
cc := UserStorageController{}
cc.as = mock.NewApp()

ch := model.UserAuthChallenge{
UserID: "u1",
DeviceID: "d1",
UserCodeChallenge: "ucc1234",
OTP: "123",
AppID: "a1",
Strategy: model.FirstFactorInternalStrategy{
Challenge: model.AuthChallengeTypeOTP,
Transport: model.AuthTransportTypeSMS,
},
}
ch, err := cc.RequestChallenge(context.TODO(), ch)
require.NoError(t, err)
_, err := cc.RequestChallenge(context.TODO(), ch)
require.Error(t, err)
require.True(t, errors.Is(err, mock.ErrNotFound))
}

// challenge is asking for otp code with sms, but app only does magic link with sms
func TestRequestSMSNoStrategyFound(t *testing.T) {
cc := UserStorageController{}
cc.as = mock.NewApp()
cc.as.CreateApp(model.AppData{
ID: "a1",
Active: true,
AuthStrategies: []model.AuthStrategy{
model.FirstFactorInternalStrategy{
Challenge: model.AuthChallengeTypeMagicLink,
Transport: model.AuthTransportTypeSMS,
},
},
})

ch := model.UserAuthChallenge{
UserID: "u1",
DeviceID: "d1",
UserCodeChallenge: "ucc1234",
OTP: "123",
AppID: "a1",
Strategy: model.FirstFactorInternalStrategy{
Challenge: model.AuthChallengeTypeOTP,
Transport: model.AuthTransportTypeSMS,
},
}
_, err := cc.RequestChallenge(context.TODO(), ch)
require.Error(t, err)
assert.True(t, errors.Is(err, l.ErrorRequestChallengeUnsupportedByAPP))
}

// challenge is asking for otp code with sms, but app only does magic link with sms
func TestRequestSMSSend(t *testing.T) {
cc := UserStorageController{}

// app storage
cc.as = mock.NewApp()
cc.as.CreateApp(model.AppData{
ID: "a1",
Active: true,
AuthStrategies: []model.AuthStrategy{
model.FirstFactorInternalStrategy{
Challenge: model.AuthChallengeTypeOTP,
Transport: model.AuthTransportTypeSMS,
},
},
})

p, _ := l.NewPrinter(language.English.String())
cc.LP = p

// auth storage
uas := &mock.UserAuthStorage{}
cc.uas = uas

// sms service
ss := &smock.SMSService{}
cc.ss = ss

// challenge
ch := model.UserAuthChallenge{
UserID: "u1",
DeviceID: "d1",
UserCodeChallenge: "ucc1234",
OTP: "123",
AppID: "a1",
Strategy: model.FirstFactorInternalStrategy{
Challenge: model.AuthChallengeTypeOTP,
Transport: model.AuthTransportTypeSMS,
},
}
_, err := cc.RequestChallenge(context.TODO(), ch)
require.NoError(t, err)
e, _ := ss.Last()
ch, _ = uas.GetLatestChallenge(context.TODO(), ch.Strategy, ch.UserID)
assert.Equal(t, fmt.Sprintf("Here is the OTP code %s, expire in 5 minutes.", ch.OTP), e)
}

0 comments on commit 1e90177

Please sign in to comment.