Skip to content

Commit

Permalink
test: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Muchogoc committed Oct 11, 2022
1 parent 467e0a4 commit 42619f3
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 52 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,10 @@ jobs:
- name: Send coverage
env:
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: goveralls -coverprofile=coverage.out -service=github
run: goveralls -coverprofile=coverage.out -service=github

- name: Quality Gate - Test coverage shall be above threshold
env:
TESTCOVERAGE_THRESHOLD: 80.0
run: |
bash scripts/coverage.sh
4 changes: 3 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,16 @@ func (s *client) refreshAccessToken() {
// MakeRequest performs a HTTP request to the provided path and parameters
func (s *client) MakeRequest(ctx context.Context, method, path string, queryParams map[string]string, body interface{}, authorised bool) (*http.Response, error) {
urlPath := fmt.Sprintf("%s%s", BaseURL, path)
var request *http.Request

var request *http.Request
switch method {
case http.MethodGet:
req, err := http.NewRequestWithContext(ctx, method, urlPath, nil)
if err != nil {
return nil, err
}
request = req

case http.MethodPost:
encoded, err := json.Marshal(body)
if err != nil {
Expand All @@ -202,6 +203,7 @@ func (s *client) MakeRequest(ctx context.Context, method, path string, queryPara
}

request = req

default:
return nil, fmt.Errorf("s.MakeRequest() unsupported http method: %s", method)

Expand Down
233 changes: 183 additions & 50 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"testing"

"github.com/brianvoe/gofakeit"
"github.com/jarcoal/httpmock"
)

Expand All @@ -25,27 +26,41 @@ func MockLogin() {
}

func TestSILComms_Login(t *testing.T) {
type fields struct {
client *http.Client
}
tests := []struct {
name string
fields fields
name string
wantPanic bool
}{
{
name: "happy case: successful login",
fields: fields{
client: &http.Client{},
},
name: "happy case: successful login",
wantPanic: false,
},
{
name: "sad case: invalid status code",
wantPanic: true,
},
{
name: "sad case: invalid api response",
wantPanic: true,
},
{
name: "sad case: invalid token response",
wantPanic: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
r := recover()
if (r != nil) != tt.wantPanic {
t.Errorf("login() recover = %v, wantPanic = %v", r, tt.wantPanic)
}
}()

httpmock.Activate()
defer httpmock.DeactivateAndReset()

if tt.name == "happy case: successful login" {
httpmock.RegisterResponder(http.MethodPost, "/auth/token/", func(r *http.Request) (*http.Response, error) {
httpmock.RegisterResponder(http.MethodPost, fmt.Sprintf("%s/auth/token/", BaseURL), func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Expand All @@ -58,34 +73,83 @@ func TestSILComms_Login(t *testing.T) {
})
}

s := &client{
client: tt.fields.client,
if tt.name == "sad case: invalid status code" {
httpmock.RegisterResponder(http.MethodPost, "/auth/token/", func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: nil,
}
return httpmock.NewJsonResponse(http.StatusBadRequest, resp)
})
}

if tt.name == "sad case: invalid api response" {
httpmock.RegisterResponder(http.MethodPost, fmt.Sprintf("%s/auth/token/", BaseURL), func(r *http.Request) (*http.Response, error) {
resp := map[string]interface{}{
"status": 1234,
"message": 1234,
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
}

if tt.name == "sad case: invalid token response" {
httpmock.RegisterResponder(http.MethodPost, fmt.Sprintf("%s/auth/token/", BaseURL), func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: map[string]interface{}{
"refresh": 1234,
"access": 1234,
},
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
}

s := newClient()

s.login()
})
}
}

func TestSILclient_refreshAccessToken(t *testing.T) {
type fields struct {
client *http.Client
refreshToken string
}
tests := []struct {
name string
fields fields
name string
wantPanic bool
}{
{
name: "happy case: refresh access token",
fields: fields{
client: &http.Client{},
},
name: "happy case: refresh access token",
wantPanic: false,
},
{
name: "sad case: invalid status code",
wantPanic: true,
},
{
name: "sad case: invalid api response",
wantPanic: true,
},
{
name: "sad case: invalid token response",
wantPanic: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
r := recover()
if (r != nil) != tt.wantPanic {
t.Errorf("refreshAccessToken() recover = %v, wantPanic = %v", r, tt.wantPanic)
}
}()

httpmock.Activate()
defer httpmock.DeactivateAndReset()
MockLogin()
s := newClient()

if tt.name == "happy case: refresh access token" {
httpmock.RegisterResponder(http.MethodPost, "/auth/token/refresh/", func(r *http.Request) (*http.Response, error) {
Expand All @@ -101,10 +165,41 @@ func TestSILclient_refreshAccessToken(t *testing.T) {
})
}

s := &client{
client: tt.fields.client,
refreshToken: tt.fields.refreshToken,
if tt.name == "sad case: invalid status code" {
httpmock.RegisterResponder(http.MethodPost, "/auth/token/refresh/", func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: nil,
}
return httpmock.NewJsonResponse(http.StatusBadRequest, resp)
})
}

if tt.name == "sad case: invalid api response" {
httpmock.RegisterResponder(http.MethodPost, fmt.Sprintf("%s/auth/token/refresh/", BaseURL), func(r *http.Request) (*http.Response, error) {
resp := map[string]interface{}{
"status": 1234,
"message": 1234,
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
}

if tt.name == "sad case: invalid token response" {
httpmock.RegisterResponder(http.MethodPost, fmt.Sprintf("%s/auth/token/refresh/", BaseURL), func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: map[string]interface{}{
"refresh": 1234,
"access": 1234,
},
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
}

s.refreshAccessToken()
})
}
Expand All @@ -125,7 +220,7 @@ func TestSILclient_MakeRequest(t *testing.T) {
wantErr bool
}{
{
name: "happy case: make authenticated request",
name: "happy case: make unauthenticated request",
args: args{
ctx: context.Background(),
method: http.MethodPost,
Expand All @@ -137,7 +232,7 @@ func TestSILclient_MakeRequest(t *testing.T) {
wantErr: false,
},
{
name: "happy case: make unauthenticated request",
name: "happy case: make authenticated POST request",
args: args{
ctx: context.Background(),
method: http.MethodPost,
Expand All @@ -148,46 +243,84 @@ func TestSILclient_MakeRequest(t *testing.T) {
},
wantErr: false,
},
{
name: "happy case: make authenticated GET request",
args: args{
ctx: context.Background(),
method: http.MethodGet,
path: "/v1/sms/bulk/",
queryParams: map[string]string{
"app": gofakeit.UUID(),
},
body: nil,
authorised: true,
},
wantErr: false,
},
{
name: "sad case: make unsupported protocol request",
args: args{
ctx: context.Background(),
method: http.MethodOptions,
path: "/v1/sms/bulk/",
queryParams: map[string]string{
"app": gofakeit.UUID(),
},
body: nil,
authorised: true,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
MockLogin()
s := newClient()

httpmock.RegisterResponder(http.MethodPost, "/v1/sms/bulk/", func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: BulkSMSResponse{
GUID: "",
Sender: "",
Message: "",
Recipients: []string{},
State: "",
SMS: []string{},
Created: "",
Updated: "",
},
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
if tt.name == "happy case: make authenticated POST request" {
httpmock.RegisterResponder(http.MethodPost, "/v1/sms/bulk/", func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: BulkSMSResponse{
GUID: "",
Sender: "",
Message: "",
Recipients: []string{},
State: "",
SMS: []string{},
Created: "",
Updated: "",
},
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
}

if tt.name == "happy case: make authenticated request" {
httpmock.RegisterResponder(http.MethodPost, "/auth/token/", func(r *http.Request) (*http.Response, error) {
if tt.name == "happy case: make authenticated GET request" {
httpmock.RegisterResponder(http.MethodGet, "/v1/sms/bulk/", func(r *http.Request) (*http.Response, error) {
resp := APIResponse{
Status: StatusSuccess,
Message: "success",
Data: TokenResponse{
Refresh: "refresh",
Access: "access",
Data: []BulkSMSResponse{
{
GUID: "",
Sender: "",
Message: "",
Recipients: []string{},
State: "",
SMS: []string{},
Created: "",
Updated: "",
},
},
}
return httpmock.NewJsonResponse(http.StatusOK, resp)
})
}

s := newClient()
got, err := s.MakeRequest(tt.args.ctx, tt.args.method, tt.args.path, tt.args.queryParams, tt.args.body, tt.args.authorised)
if (err != nil) != tt.wantErr {
t.Errorf("SILclient.MakeRequest() error = %v, wantErr %v", err, tt.wantErr)
Expand Down
8 changes: 8 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ type APIResponse struct {
Data interface{} `json:"data,omitempty"`
}

// ResultsResponse is the base response from a paginated list of results
type ResultsResponse struct {
Count int `json:"count"`
Next *string `json:"next"`
Previous *string `json:"previous"`
Results []interface{} `json:"results"`
}

// TokenResponse is the data in the API response when logging in
// The access token is used as the bearer token when making API requests
// The refresh token is used to obtain a new access token when it expires
Expand Down
12 changes: 12 additions & 0 deletions scripts/coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash
echo "Quality Gate: checking if test coverage is above threshold ..."
echo "Threshold : ${TESTCOVERAGE_THRESHOLD} %"
totalCoverage=`go tool cover -func=coverage.out | grep total | grep -Eo '[0-9]+\.[0-9]+'`
echo "Current test coverage : $totalCoverage %"
if (( $(echo "$totalCoverage ${TESTCOVERAGE_THRESHOLD}" | awk '{print ($1 >= $2)}') )); then
echo "OK"
else
echo "Current test coverage is below threshold. Please add more tests"
echo "Failed"
exit 1
fi

0 comments on commit 42619f3

Please sign in to comment.