Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions api/sms_provider/sms_provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package sms_provider

import (
"encoding/base64"
"fmt"
"net/http"
"net/url"
"testing"

"github.com/netlify/gotrue/conf"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"gopkg.in/h2non/gock.v1"
)

var handleApiRequest func(*http.Request) (*http.Response, error)

type SmsProviderTestSuite struct {
suite.Suite
Config *conf.GlobalConfiguration
}

type MockHttpClient struct {
mock.Mock
}

func (m *MockHttpClient) Do(req *http.Request) (*http.Response, error) {
return handleApiRequest(req)
}

func TestSmsProvider(t *testing.T) {
ts := &SmsProviderTestSuite{
Config: &conf.GlobalConfiguration{
Sms: conf.SmsProviderConfiguration{
Twilio: conf.TwilioProviderConfiguration{
AccountSid: "test_account_sid",
AuthToken: "test_auth_token",
MessageServiceSid: "test_message_service_id",
},
Messagebird: conf.MessagebirdProviderConfiguration{
AccessKey: "test_access_key",
Originator: "test_originator",
},
Vonage: conf.VonageProviderConfiguration{
ApiKey: "test_api_key",
ApiSecret: "test_api_secret",
From: "test_from",
},
Textlocal: conf.TextlocalProviderConfiguration{
ApiKey: "test_api_key",
Sender: "test_sender",
},
},
},
}
suite.Run(t, ts)
}

func (ts *SmsProviderTestSuite) TestTwilioSendSms() {
defer gock.Off()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually use defer gock.OffAll() so that unmatched requests don't carry over to the next test. https://pkg.go.dev/github.com/h2non/gock#OffAll

provider, err := NewTwilioProvider(ts.Config.Sms.Twilio)
require.NoError(ts.T(), err)

twilioProvider, ok := provider.(*TwilioProvider)
require.Equal(ts.T(), true, ok)

phone := "123456789"
message := "This is the sms code: 123456"

body := url.Values{
"To": {"+" + phone},
"Channel": {"sms"},
"From": {twilioProvider.Config.MessageServiceSid},
"Body": {message},
}

cases := []struct {
Desc string
TwilioResponse *gock.Response
ExpectedError error
}{
{
Desc: "Successfully sent sms",
TwilioResponse: gock.New(twilioProvider.APIPath).Post("").
MatchHeader("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(twilioProvider.Config.AccountSid+":"+twilioProvider.Config.AuthToken))).
MatchType("url").BodyString(body.Encode()).
Reply(200).JSON(SmsStatus{
To: "+" + phone,
From: twilioProvider.Config.MessageServiceSid,
Status: "sent",
Body: message,
}),
ExpectedError: nil,
},
{
Desc: "Sms status is failed / undelivered",
TwilioResponse: gock.New(twilioProvider.APIPath).Post("").
MatchHeader("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(twilioProvider.Config.AccountSid+":"+twilioProvider.Config.AuthToken))).
MatchType("url").BodyString(body.Encode()).
Reply(200).JSON(SmsStatus{
ErrorMessage: "failed to send sms",
ErrorCode: "401",
Status: "failed",
}),
ExpectedError: fmt.Errorf("twilio error: %v %v", "failed to send sms", "401"),
},
{
Desc: "Non-2xx status code returned",
TwilioResponse: gock.New(twilioProvider.APIPath).Post("").
MatchHeader("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(twilioProvider.Config.AccountSid+":"+twilioProvider.Config.AuthToken))).
MatchType("url").BodyString(body.Encode()).
Reply(500).JSON(twilioErrResponse{
Code: 500,
Message: "Internal server error",
MoreInfo: "error",
Status: 500,
}),
ExpectedError: &twilioErrResponse{
Code: 500,
Message: "Internal server error",
MoreInfo: "error",
Status: 500,
},
},
}

for _, c := range cases {
ts.Run(c.Desc, func() {
err = twilioProvider.SendSms(phone, message)
require.Equal(ts.T(), c.ExpectedError, err)
})
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another good check at the end of a test is assert.Empty(t, gock.GetUnmatchedRequests()). It ensures that all expectations you've set with gock are called once.

I also find its default logging a little unclear, so I created a helper to print out unmatched requests.

}

func (ts *SmsProviderTestSuite) TestMessagebirdSendSms() {
defer gock.Off()
provider, err := NewMessagebirdProvider(ts.Config.Sms.Messagebird)
require.NoError(ts.T(), err)

messagebirdProvider, ok := provider.(*MessagebirdProvider)
require.Equal(ts.T(), true, ok)

phone := "123456789"
message := "This is the sms code: 123456"
body := url.Values{
"originator": {messagebirdProvider.Config.Originator},
"body": {message},
"recipients": {phone},
"type": {"sms"},
"datacoding": {"unicode"},
}
gock.New(messagebirdProvider.APIPath).Post("").MatchHeader("Authorization", "AccessKey "+messagebirdProvider.Config.AccessKey).MatchType("url").BodyString(body.Encode()).Reply(200).JSON(MessagebirdResponse{
Recipients: MessagebirdResponseRecipients{
TotalSentCount: 1,
},
})

err = messagebirdProvider.SendSms(phone, message)
require.NoError(ts.T(), err)
}

func (ts *SmsProviderTestSuite) TestVonageSendSms() {
defer gock.Off()
provider, err := NewVonageProvider(ts.Config.Sms.Vonage)
require.NoError(ts.T(), err)

vonageProvider, ok := provider.(*VonageProvider)
require.Equal(ts.T(), true, ok)

phone := "123456789"
message := "This is the sms code: 123456"

body := url.Values{
"from": {vonageProvider.Config.From},
"to": {phone},
"text": {message},
"api_key": {vonageProvider.Config.ApiKey},
"api_secret": {vonageProvider.Config.ApiSecret},
}

gock.New(vonageProvider.APIPath).Post("").MatchType("url").BodyString(body.Encode()).Reply(200).JSON(VonageResponse{
Messages: []VonageResponseMessage{
{Status: "0"},
},
})

err = vonageProvider.SendSms(phone, message)
require.NoError(ts.T(), err)
}

func (ts *SmsProviderTestSuite) TestTextLocalSendSms() {
defer gock.Off()
provider, err := NewTextlocalProvider(ts.Config.Sms.Textlocal)
require.NoError(ts.T(), err)

textlocalProvider, ok := provider.(*TextlocalProvider)
require.Equal(ts.T(), true, ok)

phone := "123456789"
message := "This is the sms code: 123456"
body := url.Values{
"sender": {textlocalProvider.Config.Sender},
"apikey": {textlocalProvider.Config.ApiKey},
"message": {message},
"numbers": {phone},
}

gock.New(textlocalProvider.APIPath).Post("").MatchType("url").BodyString(body.Encode()).Reply(200).JSON(TextlocalResponse{
Status: "success",
Errors: []TextlocalError{},
})

err = textlocalProvider.SendSms(phone, message)
require.NoError(ts.T(), err)
}
2 changes: 1 addition & 1 deletion api/sms_provider/textlocal.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (t *TextlocalProvider) SendSms(phone string, message string) error {
return derr
}

if len(resp.Errors) == 0 {
if len(resp.Errors) > 0 {
return errors.New("textlocal error: Internal Error")
}

Expand Down
1 change: 0 additions & 1 deletion api/sms_provider/twilio.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func (t *TwilioProvider) SendSms(phone string, message string) error {
"From": {t.Config.MessageServiceSid},
"Body": {message},
}

client := &http.Client{Timeout: defaultTimeout}
r, err := http.NewRequest("POST", t.APIPath, strings.NewReader(body.Encode()))
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ require (
github.com/google/go-cmp v0.5.2 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
Expand Down Expand Up @@ -93,6 +94,7 @@ require (
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/h2non/gock.v1 v1.1.2 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -421,6 +423,7 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nats-io/stan.go v0.4.5/go.mod h1:Ji7mK6gRZJSH1nc3ZJH6vi7zn/QnZhpR9Arm4iuzsUQ=
github.com/nats-io/stan.go v0.5.0/go.mod h1:dYqB+vMN3C2F9pT1FRQpg9eHbjPj6mP0yYuyBNuXHZE=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/netlify/mailme v1.1.1 h1:S/ANl+Hy/EIoJUgGiLJYYLZJ2QOTG452R73qTQudMns=
github.com/netlify/mailme v1.1.1/go.mod h1:8g03BJmU+ps7ma5vcH+t8aMtaicQTMX3ffP7RJ8xY8g=
github.com/netlify/netlify-commons v0.32.0 h1:IgpqedBa6aFrc+daRgGZ+SmU9eBXlDXzKSAjevWmshM=
Expand Down Expand Up @@ -869,6 +872,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
Expand Down